2010-05-12 3 views
21

Может ли кто-нибудь предложить контейнер Go для простой и быстрой FIF/очереди, Go имеет 3 разных контейнера: heap, list и vector. Какой из них более подходит для реализации очереди?Есть ли реализация очереди?

ответ

10

Любой вектор или список должны работать, но вектор - это, вероятно, путь. Я говорю это, потому что вектор, вероятно, будет выделяться реже, чем сбор списков и мусора (в текущей реализации Go) довольно дорого. Однако в небольшой программе это, вероятно, не имеет значения.

+8

ПРИМЕЧАНИЕ: Перейти 1 удаляет контейнер/векторный пакет прямой. Код, который использует контейнер/вектор, должен быть обновлен для непосредственного использования фрагментов. [Перейти к 1 Примечания к выпуску] (http://golang.org/doc/go1): [Удаленные пакеты] (http://golang.org/doc/go1#deleted). [SliceTricks Как сделать векторные объекты с помощью фрагментов] (https://code.google.com/p/go-wiki/wiki/SliceTricks). – peterSO

7

Для расширения на стороне реализации, Moraes предлагает в his gist некоторые из очереди-структуру и стек:

// Stack is a basic LIFO stack that resizes as needed. 
type Stack struct { 
    nodes []*Node 
    count int 
} 
// Queue is a basic FIFO queue based on a circular list that resizes as needed. 
type Queue struct { 
    nodes []*Node 
    head int 
    tail int 
    count int 
} 

Вы можете увидеть его в действии в этом playground example.

+0

Этот метод настолько плохо разработан, хотя =/ – Sir

30

На самом деле, если вам нужна простая и простая в использовании очередь fifo, срез предоставляет все, что вам нужно.

queue := make([]int, 0) 
// Push 
queue := append(queue, 1) 
// Top (just get next element, don't remove it) 
x = queue[0] 
// Discard top element 
queue = queue[1:] 
// Is empty ? 
if len(queue) == 0 { 
    fmt.Println("Queue is empty !") 
} 

Конечно, мы предполагаем, что мы можем доверять внутренней реализации Append и нарезку, так что избежать бесполезного изменения размера и перераспределить. Для основного использования это вполне достаточно.

+3

[SliceTricks: как делать векторные объекты с помощью срезов] (https://code.google.com/p/go-wiki/wiki/SliceTricks). – peterSO

+0

Проблема в том, что элемент будет скопирован довольно часто. Это может быть не проблема (копирование происходит быстро), но это то, что нужно помнить. – Florian

+1

@Florian, этот пример кода использует '[] int', где копирование - это то, что вы хотите. Если вместо этого это было 'type Foo struct {/ * lots of fields * /}', вы обычно использовали бы '[] * Foo' и должны быть указатели очереди, и вы бы не копировали элементы вообще как раз указатель.(Затем вы также захотите сделать 'queue [0] = nil; queue = queue [1:]', чтобы отбросить первый элемент и удалить ссылку на него из очереди). –

22

Удивлен, что никто не предлагал буферизованные каналы, так как в любом случае FIFO Queue с ограниченным размером.

//Or however many you might need + buffer. 
c := make(chan int, 300) 

//Push 
c <- value 

//Pop 
x <- c 
+3

Для небольших очередей, которые не нуждаются в постоянстве, это должно быть по умолчанию. Даже если вы записываете в неограниченную очередь на диске, запись и чтение с канала часто являются хорошим способом сделать это. – Rob

+0

Действительно, это кажется самым простым и самым логичным для меня. – Eric

+0

Не x = <- c блокирующий вызов? Если c пуст, ваша маршрутизация может зависать до тех пор, пока очередь не будет пополнена. Это не то поведение, которое требуется для простой структуры данных очереди? – anaken78

2

Используя кусочек и соответствующий («круговой») схема индексации сверху еще, кажется, путь. Вот мой пример: https://github.com/phf/go-queue Тесты там также подтверждают, что каналы быстрее, но по цене более ограниченной функциональности.

+1

Это был бы более хороший ответ, если бы он выписал некоторые из наиболее релевантных кодов из вашего репо. –

+1

Я предположил, что тот, кто хочет увидеть код, просто нажмет на ссылку. Извините, я здесь совершенно новый. Я обновляю ответ с некоторыми фрагментами. –

+0

Не поймите меня неправильно, это не плохой ответ, как есть, и, конечно же, он не будет удален, поскольку некоторые поверхностно похожие ответы «только для ссылок», но это может быть немного лучше, чем с большим количеством того же: объяснения реального кода, которые являются наиболее важными для ответа SO. –

0

Ломтик может использоваться для реализации очереди.

type queue struct { 
    values []*int 
} 

func New() *queue { 
    queue := &queue{} 
    return queue 
} 

func (q *queue) enqueue(val *int) { 
    q.values = append(q.values, val) 
} 

//deque function 

Update:

здесь завершена реализация на моей странице GitHub https://github.com/raiskumar/algo-ds/blob/master/tree/queue.go

+0

aand где dequeue? –

+0

Исключена реализация намеренно (используйте метод enqueue, чтобы понять, как будет выполняться dequeue) :) –

+0

Вы имеете в виду q.values ​​= q.values ​​[i:]? Это потеряет память. –

1

Я также реализовать очередь из ломтика, как описано выше. Тем не менее, он не является потокобезопасным. Поэтому я решил добавить блокировку (блокировку мьютекса), чтобы гарантировать потокобезопасность.

package queue 

import (
    "sync" 
) 

type Queue struct { 
    lock *sync.Mutex 
    Values []int 
} 

func Init() Queue { 
    return Queue{&sync.Mutex{}, make([]int, 0)} 
} 

func (q *Queue) Enqueue(x int) { 
    for { 
    q.lock.Lock() 
    q.Values = append(q.Values, x) 
    q.lock.Unlock() 
    return 
    } 
} 

func (q *Queue) Dequeue() *int { 
    for { 
    if (len(q.Values) > 0) { 
     q.lock.Lock() 
     x := q.Values[0] 
     q.Values = q.Values[1:] 
     q.lock.Unlock() 
     return &x 
    } 
    return nil 
    } 
    return nil 
} 

Вы можете проверить мое решение на GitHub здесь simple queue