Подтвердить что ты не робот

Лучшая реализация очереди Java?

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

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

Теперь все в порядке и dandy-, но учитывая тот факт, что его очередь будет анализировать ТЫСЯЧИ пикселей за очень короткий промежуток времени, при этом постоянно нажимая и нажимая, БЕЗ поддержания предсказуемого состояния (это может быть где-то между длиной 100 и 20000), реализация очереди должна иметь значительно быстрые способности "выталкивать и выдвигать".

Связанный список кажется привлекательным из-за его способности вставлять элементы в себя, не переставляя ничего в списке, но для того, чтобы он был достаточно быстрым, ему потребуется легкий доступ как к его голове, так и к его хвосту (или второму последний узел, если бы он не был дважды связан). К сожалению, я не могу найти какую-либо информацию, связанную с базовой реализацией связанных списков в Java, поэтому трудно сказать, действительно ли связанный список - это путь...

Это подводит меня к моему вопросу. Какова будет лучшая реализация интерфейса очереди в Java для того, что я намерен сделать? (Я не хочу редактировать или даже получать доступ к чему-либо, кроме заголовка и хвоста очереди - я не хочу делать какие-либо перестановки или что-то в этом роде. С другой стороны, я НАМЕРЕН делать много нажатий и выскочить, и очередь будет немного менять размер, поэтому предварительное распределение будет неэффективным)

4b9b3361

Ответ 1

LinkedList, кажется, подходит, LinkedList представляет собой двусвязный список, который подходит для структуры данных Queue (FIFO).

Он поддерживает ссылки на элементы Head и Tail, которые вы можете получить с помощью .getFirst() и .getLast() соответственно.

Вы также можете использовать .push(E e) для добавления элемента в конец очереди и .pop() для удаления из очереди и извлечения последнего элемента очереди.

Ответ 2

Если вы используете LinkedList, будьте осторожны. Если вы используете его следующим образом:

LinkedList<String> queue = new LinkedList<String>();

то вы можете нарушить определение очереди, потому что можно удалить другие элементы, чем сначала (в LinkedList есть такие методы).

Но если вы используете его так:

Queue<String> queue = new LinkedList<String>();

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

Вы можете преодолеть дефектную реализацию интерфейса Queue, расширив класс LinkedList до класса PureQueue, который выдает UnsupportedOperationException любого из методов оскорбления. Или вы можете принять подход с aggreagation, создав PureQueue только с одним полем, которое является объектом LinkedList типа, списком и единственными методами будет конструктор по умолчанию, конструктор копирования, isEmpty(), size(), add(E element), remove() и element(). Все эти методы должны быть однострочными, например:

/**
* Retrieves and removes the head of this queue.
* The worstTime(n) is constant and averageTime(n) is constant.
*
* @return the head of this queue.
* @throws NoSuchElementException if this queue is empty.
*/
public E remove()
{
    return list.removeFirst();
} // method remove()

Ответ 3

Ознакомьтесь с интерфейсом Deque, который предусматривает вставку/удаление с обоих концов. LinkedList реализует этот интерфейс (как упоминалось выше), но для вашего использования ArrayDeque может быть лучше - вы не понесете стоимость постоянных распределений объектов для каждого node. Опять же, может не иметь значения, какую реализацию вы используете.

Вступает в игру нормальная полиморфность: красота письма против интерфейса Deque, а не какая-либо конкретная реализация, заключается в том, что вы можете очень легко переключать реализации, чтобы проверить, какой из них лучше всего работает. Просто измените строку с new в ней, а остальная часть кода останется прежней.

Ответ 4

Лучше использовать ArrayDeque вместо LinkedList при реализации Stack и Queue в Java. ArrayDeque, скорее всего, будет быстрее, чем интерфейс Stack (в то время как Stack является потокобезопасным) при использовании в качестве стека и быстрее, чем LinkedList при использовании в качестве очереди. Посмотрите на эту ссылку Используйте ArrayDeque вместо LinkedList или Stack.

Ответ 5

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

Ответ 6

Я думаю, что вы можете с такой простой реализацией

package DataStructures;

public class Queue<T> {

   private Node<T> root;

   public Queue(T value) {
      root = new Node<T>(value);
   }

   public void enque(T value) {
      Node<T> node = new Node<T>(value);
      node.setNext(root);
      root = node;
   }

   public Node<T> deque() {

      Node<T> node = root;
      Node<T> previous = null;

      while(node.next() != null) {
         previous = node;
         node = node.next();
      }
      node = previous.next();
      previous.setNext(null);
      return node;
   }

   static class Node<T> {

      private T value;
      private Node<T> next;

      public Node (T value) {
         this.value = value;
      }

      public void setValue(T value) {
         this.value = value;
      }

      public T getValue() {
         return value;
      }

      public void setNext(Node<T> next) {
         this.next = next;
      }

      public Node<T> next() {
         return next;
      }
   }
}

Ответ 7

Однако, если вы все еще хотите использовать рекурсивный алгоритм, вы можете изменить его как "tail-recursive" , который, вероятно, оптимизирован в JVM, чтобы избежать.

Ответ 8

O (1) доступ к первому и последнему узлам.

class Queue {

private Node head;
private Node end;

public void enqueue(Integer data){

    Node node = new Node(data);
    if(this.end == null){
        this.head = node;
        this.end = this.head;
    }
    else {
        this.end.setNext(node);
        this.end = node;
    }
}

public void dequeue (){

    if (head == end){
        end = null;
    }

    head = this.head.getNext();
}


@Override
public String toString() {
    return head.getData().toString();
}

public String deepToString() {

    StringBuilder res = new StringBuilder();
    res.append(head.getData());

    Node cur = head;
    while (null != (cur = cur.getNext())){
        res.append(" ");
        res.append(cur.getData());

    }
    return res.toString();
}

}

class Node {

private Node next;

private Integer data;


Node(Integer i){
    data = i;
}

public Integer getData() {
    return data;
}

public Node getNext() {
    return next;
}

public void setNext(Node next) {
    this.next = next;
}

}

Ответ 9

Вот реализация очереди с итератором и интерфейсом Iterable

Размер очереди будет увеличиваться по мере заполнения

Интерфейс очереди

package com.practice.ds.queue;

import com.practice.ds.queue.exception.QueueException;

public interface QueueInterface<T> {

    public boolean empty();

    public void enqueue(T item);

    public void dequeue() throws QueueException;

    public T front() throws QueueException;

    public void clear();
}

Класс пользовательских исключений

package com.practice.ds.queue.exception;

public class QueueException extends Exception {

    private static final long serialVersionUID = -884127093599336807L;

    public QueueException() {
        super();
    }

    public QueueException(String message) {
        super(message);
    }

    public QueueException(Throwable e) {
        super(e);
    }

    public QueueException(String message, Throwable e) {
        super(message, e);
    }
}

Реализация очереди

package com.practice.ds.queue;

import java.util.Iterator;

import com.practice.ds.queue.exception.QueueException;

public class Queue<T> implements QueueInterface<T>, Iterable<T> {

    private static final int DEFAULT_CAPACITY = 10;
    private int current = 0;
    private int rear = 0;
    private T[] queueArray = null;
    private int capacity = 0;

    @SuppressWarnings("unchecked")
    public Queue() {
        capacity = DEFAULT_CAPACITY;
        queueArray = (T[]) new Object[DEFAULT_CAPACITY];
        rear = 0;
        current = 0;
    }

    @Override
    public boolean empty() {
        return capacity == current;
    }

    @Override
    public void enqueue(T item) {
        if(full())
            ensureCapacity();
        queueArray[current] = item;
        current++;
    }

    @Override
    public void dequeue() throws QueueException {
        T dequeuedItem = front();
        rear++;
        System.out.println("Dequed Item is " + dequeuedItem);
    }

    @Override
    public T front() throws QueueException {
        return queueArray[rear];
    }

    @Override
    public void clear() {
        for (int i = 0; i < capacity; i++)
            queueArray[i] = null;
        current = 0;
        rear = 0;
    }

    @SuppressWarnings("unchecked")
    private void ensureCapacity() {
        if (rear != 0) {
            copyElements(queueArray);
        } else {
            capacity *= 2;
            T[] tempQueueArray = (T[]) new Object[capacity];
            copyElements(tempQueueArray);
        }
        current -= rear;
        rear = 0;
    }

    private void copyElements(T[] array) {
        for (int i = rear; i < current; i++)
            array[i - rear] = queueArray[i];
        queueArray = array;
    }

    @Override
    public Iterator<T> iterator() {
        return new QueueItearator<T>();
    }

    public boolean full() {
        return current == capacity;
    }

    private class QueueItearator<T> implements Iterator<T> {

        private int index = rear;

        @Override
        public boolean hasNext() {
            return index < current;
        }

        @SuppressWarnings("unchecked")
        @Override
        public T next() {
            return (T) queueArray[index++];
        }
    }

}