Предоставляют ли какие-либо библиотеки 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)


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

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

Я столкнулся с этой проблемой и попытался решить ее, скопировав исходный код 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

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

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

ОБНОВЛЕНИЕ:

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

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

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

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

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

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

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

person Will Hartung    schedule 05.11.2009

Единственная библиотека, которая, как мне кажется, реализует такой интерфейс, это 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

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

person Chris B.    schedule 05.11.2009

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

person Derek Lewis    schedule 05.11.2009

Биномиальная куча может иметь 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