Предоставляют ли какие-либо библиотеки Java реализацию очереди произвольного доступа?

Я реализую скользящее окно над потоком событий на Java. Итак, я хочу структуру данных, которая позволяет мне делать следующее:

  1. добавлять в конец структуры данных при возникновении новых событий;

  2. удалять из начала структуры данных при обработке старых событий;

  3. получить стандартный произвольный доступ (size(), get(i)) к элементам структуры данных; в общем, типичный List "прочитан" операции;

  4. эффективен для всех вышеперечисленных операций;

  5. неограничен.

Другой доступ не требуется. И никакой потокобезопасности не требуется.

В настоящее время я делаю это с ArrayList, чтобы привести все в порядок. Но я хочу что-то более эффективное; метод remove(0) (2. выше) неэффективен с ArrayList.

Номера 1. и 2. являются стандартными Queue операции в стиле. Однако реализации Queue в JDK (например, ArrayDeque) не допускайте get(i) в 3.

Итак, мне интересно, есть ли какие-либо библиотеки, которые имеют такую ​​реализацию и подходят для коммерческого использования.

Если нет, я думаю, я прибегну к написанию своего собственного...


person Calum    schedule 05.11.2009    source источник
comment
Насколько часты эти операции? Возможно, вам придется обменять серьезное пространство на время - например. вы могли бы использовать циклически связанный список с индексом для быстрого поиска (индекс: я забыл название этой структуры; двоичное дерево указателей на 0 и n/2, каждый из которых хранит указатели на середину своей половины и т. д. ).   -  person Alex Feinman    schedule 05.11.2009


Ответы (8)


arrow_upward
4
arrow_downward

Похоже на задачу для Циклического буфера — если очередь имеет фиксированный вместимость. Однако я не знаю ни одной стандартной реализации. Но вот хороший рецепт, как сделать свой собственный.

person sfussenegger    schedule 05.11.2009
comment
Забыл сказать, что он должен быть неограниченным - сейчас обновил вопрос. - person Calum; 05.11.2009
comment
При необходимости вы можете увеличить емкость, например, как это делает arraylist. Хотя это дорого - person sfussenegger; 05.11.2009
comment
Удвоение размера массива, когда вам нужна большая емкость, по-прежнему делает вставку амортизированной за постоянное время, поэтому это дорого, только если вам нужны гарантии жесткой задержки. Вопрос был о Java, где сборщик мусора обычно имеет большее значение для непредсказуемой задержки, чем удвоение массива. Я бы сказал, что круговой буфер является правильным ответом на требования. - person Jamey Sharp; 06.11.2012

arrow_upward
4
arrow_downward

Я столкнулся с этой проблемой и попытался решить ее, скопировав исходный код ArrayDeque и добавив что-то вроде:

E get(index){ return elements[(head + index) % size];}
person Jacky Li    schedule 16.11.2011
comment
Спасибо, я также только что портировал ArrayDeque, чтобы использовать его на Android API уровня 8. Мне просто нужно было добавить следующий метод: public E get(int index) {return elements[(head + index) % size()];} - person gsingh2011; 11.11.2012
comment
На самом деле, код, который я разместил, иногда приводил к нулевому указателю. Этот код работает, но я не знаю почему: public E get(int index) {return elements[(head + index) % elements.length];} - person gsingh2011; 11.11.2012
comment
@gsingh2011: Внутри очередь поддерживается массивом с предварительно выделенным размером, который равен elements. Таким образом, elements.length возвращает размер внутреннего массива вместо size() самой очереди. - person FuzzY; 03.06.2015

arrow_upward
3
arrow_downward

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

Итак, у вас есть круговой массив, поддерживающий функции вставки и удаления.

ОБНОВЛЕНИЕ:

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

person James Black    schedule 05.11.2009
comment
В частности, удвоение размера массива, когда вам нужна большая емкость, по-прежнему делает вставку амортизированной за постоянное время. Это здравый подход. - person Jamey Sharp; 06.11.2012

arrow_upward
2
arrow_downward

Как быстро события входят в эту очередь и выходят из нее?

С одной стороны, вы можете иметь «достаточно большой» кольцевой буфер.

Хотя технически он «ограничен», вы можете сделать его «неограниченным», увеличив его по мере необходимости.

Точно так же вы можете «сжать» его с точки зрения общей емкости, когда он «тихий».

Но для многих приложений кольцевой буфер емкостью 100, 1000 или даже 10000 элементов практически не ограничен.

person Will Hartung    schedule 05.11.2009

arrow_upward
1
arrow_downward

Единственная библиотека, которая, как мне кажется, реализует такой интерфейс, это LinkedList, но, честно говоря, я не уверен, каковы характеристики производительности.

person Jasoon    schedule 05.11.2009
comment
Это не позволит обеспечить эффективный произвольный доступ — вызов get(i). - person Calum; 05.11.2009
comment
Очень верно. Мне было бы интересно посмотреть, что здесь происходит, так как я не уверен, как вы могли бы эффективно использовать как эффективный произвольный доступ, так и скорость, которую вы получаете от стандартной очереди FIFO болота... - person Jasoon; 05.11.2009
comment
ArrayDeque в JDK (очередь) на самом деле имеет массив элементов (размер которых можно изменить) и поля начала и конца. Таким образом, это является подходящей реализацией, но она не предоставляет элементы в виде произвольного доступа — она предоставляет только начало и конец. s голова и хвост - person Calum; 05.11.2009
comment
Возможный подход с LinkedList — это Deque, но как список его можно перетасовать с помощью Collections.shuffle() — поэтому, в зависимости от потребностей в произвольном доступе, можно выполнить одну операцию перетасовки, а затем вытолкнуть/удалить/ повторять столько случайных элементов, сколько необходимо. stackoverflow.com/a/19259094/7207622 - person JeremyDouglass; 19.03.2020

arrow_upward
1
arrow_downward

Просто выбрасываю это как альтернативу созданию собственного, поэтому, пожалуйста, примите это с недоверием: в зависимости от того, как часто вам нужен произвольный доступ get(i) и какая производительность вам от него нужна (и насколько велика будет ваша очередь в целом). , вы всегда можете использовать ArrayDeque.toArray()[i], когда вам нужно получить доступ к элементу. toArray() использует System.arraycopy() под прикрытием, что должно быть довольно быстрым для небольших размеров очереди и случайного использования. Помогло бы понять, зачем вам нужен произвольный доступ к очереди и как часто он нужен - возможно, есть другой способ реализовать ваш алгоритм без него.

person Chris B.    schedule 05.11.2009

arrow_upward
1
arrow_downward

Если оно действительно должно быть неограниченным, то что-то вроде ConcurrentSkipListMap может оказаться полезным, если вы назначите возрастающую последовательность каждому событию, чтобы использовать его в качестве ключа на карте. Он предоставил такие методы, как pollFirst/LastEntry. Если вы можете пожертвовать его неограниченной природой, тогда кольцевой буфер может быть тем, что вам нужно.

person Derek Lewis    schedule 05.11.2009

arrow_upward
0
arrow_downward

Биномиальная куча может иметь O(1) амортизированную вставку и O(log n) амортизированную операцию удаления min; Я считаю, что он также может иметь O (log ** 2 n) амортизированный произвольный доступ. Queue-push вставит элемент в кучу с последовательными целыми числами в качестве ключей.

С помощью rbtree вы можете выполнить отправку в очередь с пессимистичным O (log n) для всех операций вставки, удаления min и произвольного доступа. Это связано с тем, что дерево будет иметь непрерывный набор целых чисел в качестве ключей, а k-й элемент очереди будет элементом дерева с k-м ключом.

person Adrian Panasiuk    schedule 05.11.2009