Czy istnieje kolejka o stałym rozmiarze, która usuwa nadmiar elementów?


Odpowiedzi:


19

Nie ma istniejącej implementacji w języku Java i środowisku wykonawczym. Wszystkie kolejki rozszerzają AbstractQueue , a jego dokument jasno stwierdza, że ​​dodanie elementu do pełnej kolejki zawsze kończy się wyjątkiem. Najlepszym rozwiązaniem (i całkiem prostym) byłoby umieszczenie kolejki we własnej klasie, aby mieć potrzebną funkcjonalność.

Ponownie, ponieważ wszystkie kolejki są dziećmi AbstractQueue, po prostu użyj tego jako wewnętrznego typu danych i powinieneś mieć elastyczną implementację działającą praktycznie w mgnieniu oka :-)

AKTUALIZACJA:

Jak opisano poniżej, dostępne są dwie otwarte implementacje (ludzie, ta odpowiedź jest dość stara!) . Szczegółowe informacje można znaleźć w tej odpowiedzi .


4
Użyj Queue zamiast AbstractQueue ... mogą istnieć kolejki, które implementują interfejs, ale nie rozszerzają klasy abstrakcyjnej.
TofuBeer

1
W Pythonie możesz użyć collection.dequez określonym maxlen.
Jonas Gröger

2
AKTUALIZACJA Dostępne są teraz dwie takie klasy. Nie musisz pisać własnego. Zobacz moją odpowiedź na tej stronie.
Basil Bourque

107

Właściwie LinkedHashMap robi dokładnie to, co chcesz. Musisz nadpisać removeEldestEntrymetodę.

Przykład kolejki z maksymalnie 10 elementami:

  queue = new LinkedHashMap<Integer, String>()
  {
     @Override
     protected boolean removeEldestEntry(Map.Entry<Integer, String> eldest)
     {
        return this.size() > 10;   
     }
  };

Jeśli „removeEldestEntry” zwróci wartość true, najstarszy wpis zostanie usunięty z mapy.


7
to właściwie nie robi tego, co robi kolejka, jak mogę pobrać najnowsze. obiekt?
Alex

69

Tak, dwa

Z mojego zduplikowanego pytania z tą poprawną odpowiedzią dowiedziałem się o dwóch:


Robiłem produktywny użytek z guawy EvictingQueue , pracowałem dobrze.

Aby utworzyć wystąpienie EvictingQueuewywołania statycznej metody fabryki createi określić maksymalny rozmiar.

EvictingQueue< Person > people = com.google.common.collect.EvictingQueue.create( 100 ) ;  // Set maximum size to 100. 

... a jeśli nie możesz użyć Commons Collection 4.0, CircularFifoBuffer wydaje się być podobny do CircularFifoQueue w wersji 3.0.
Sridhar Sarnobat

CircularFifoQueuelink jest martwy, użyj zamiast tego commons.apache.org/proper/commons-collections/apidocs/org/…
user7294900

@ user7294900 Dzięki, link poprawiony. Do Twojej wiadomości, Stack Overflow zaprasza Cię do samodzielnego wprowadzania takich zmian bezpośrednio w odpowiedzi. Edytować może każdy, nie tylko oryginalny autor. Stack Overflow ma pod tym względem bardziej polubić Wikipedię.
Basil Bourque

@BasilBourque tak, ale takie zmiany mogą być odrzucone nawet przeze mnie przy zmianie linków to szara strefa
user7294900

18

Właśnie zaimplementowałem kolejkę o stałym rozmiarze w ten sposób:

public class LimitedSizeQueue<K> extends ArrayList<K> {

    private int maxSize;

    public LimitedSizeQueue(int size){
        this.maxSize = size;
    }

    public boolean add(K k){
        boolean r = super.add(k);
        if (size() > maxSize){
            removeRange(0, size() - maxSize);
        }
        return r;
    }

    public K getYoungest() {
        return get(size() - 1);
    }

    public K getOldest() {
        return get(0);
    }
}

1
Powinno byćremoveRange(0, size() - maxSize)
Ahmed Hegazy,

@AhmedHegazy removeRange (0, size () - maxSize - 1) jest poprawne
Ashish Doneriya

Zgadzam się z Amhed powyżej. Usuń - 1. W przeciwnym razie przy maksymalnej pojemności otrzymasz tablicę o maksymalnym rozmiarze + 1, ponieważ mówimy o opartej na 0. Na przykład. Jeśli maxSize = 50, to podczas dodawania nowego obiektu formuła removeRange w oryginalnym poście będzie miała wartość 51 - 50 - 1 = 0 (tj. Nic nie zostało usunięte).
Etep

8

To jest to, co zrobiłem z Queueopakowaniem LinkedList, ma stały rozmiar, który podam tutaj to 2;

public static Queue<String> pageQueue;

pageQueue = new LinkedList<String>(){
            private static final long serialVersionUID = -6707803882461262867L;

            public boolean add(String object) {
                boolean result;
                if(this.size() < 2)
                    result = super.add(object);
                else
                {
                    super.removeFirst();
                    result = super.add(object);
                }
                return result;
            }
        };


....
TMarket.pageQueue.add("ScreenOne");
....
TMarket.pageQueue.add("ScreenTwo");
.....

4

Myślę, że to, co opisujesz, to cykliczna kolejka. Oto przykład i tutaj jest lepszy jeden


4

Ta klasa spełnia swoje zadanie, wykorzystując kompozycję zamiast dziedziczenia (inne odpowiedzi tutaj), co eliminuje możliwość wystąpienia pewnych skutków ubocznych (opisanych przez Josha Blocha w Essential Java). Przycinanie podstawowej LinkedList odbywa się w metodach add, addAll i offer.

import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;

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

    private final int limit;
    private final LinkedList<T> list = new LinkedList<T>();

    public LimitedQueue(int limit) {
        this.limit = limit;
    }

    private boolean trim() {
        boolean changed = list.size() > limit;
        while (list.size() > limit) {
            list.remove();
        }
        return changed;
    }

    @Override
    public boolean add(T o) {
        boolean changed = list.add(o);
        boolean trimmed = trim();
        return changed || trimmed;
    }

    @Override
    public int size() {
        return list.size();
    }

    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }

    @Override
    public boolean contains(Object o) {
        return list.contains(o);
    }

    @Override
    public Iterator<T> iterator() {
        return list.iterator();
    }

    @Override
    public Object[] toArray() {
        return list.toArray();
    }

    @Override
    public <T> T[] toArray(T[] a) {
        return list.toArray(a);
    }

    @Override
    public boolean remove(Object o) {
        return list.remove(o);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        return list.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        boolean changed = list.addAll(c);
        boolean trimmed = trim();
        return changed || trimmed;
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        return list.removeAll(c);
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        return list.retainAll(c);
    }

    @Override
    public void clear() {
        list.clear();
    }

    @Override
    public boolean offer(T e) {
        boolean changed = list.offer(e);
        boolean trimmed = trim();
        return changed || trimmed;
    }

    @Override
    public T remove() {
        return list.remove();
    }

    @Override
    public T poll() {
        return list.poll();
    }

    @Override
    public T element() {
        return list.element();
    }

    @Override
    public T peek() {
        return list.peek();
    }
}

3
public class CircularQueue<E> extends LinkedList<E> {
    private int capacity = 10;

    public CircularQueue(int capacity){
        this.capacity = capacity;
    }

    @Override
    public boolean add(E e) {
        if(size() >= capacity)
            removeFirst();
        return super.add(e);
    }
}

Użytkowanie i wynik testu:

public static void main(String[] args) {
    CircularQueue<String> queue = new CircularQueue<>(3);
    queue.add("a");
    queue.add("b");
    queue.add("c");
    System.out.println(queue.toString());   //[a, b, c]

    String first = queue.pollFirst();       //a
    System.out.println(queue.toString());   //[b,c]

    queue.add("d");
    queue.add("e");
    queue.add("f");
    System.out.println(queue.toString());   //[d, e, f]
}

0

Brzmi jak zwykła lista, w której metoda add zawiera dodatkowy fragment kodu, który obcina listę, jeśli stanie się zbyt długa.

Jeśli jest to zbyt proste, prawdopodobnie konieczna będzie edycja opisu problemu.


Właściwie musiałby usunąć pierwszy element (tj. Najwcześniejszy), obcięcie usuwałoby ostatni element. Nadal praktyczne dzięki LinkedList.
MAK


0

Nie jest do końca jasne, jakie masz wymagania, które skłoniły Cię do zadania tego pytania. Jeśli potrzebujesz struktury danych o stałym rozmiarze, możesz również przyjrzeć się różnym zasadom buforowania. Ponieważ jednak masz kolejkę, przypuszczam, że szukasz jakiejś funkcji routera. W takim przypadku wybrałbym bufor pierścieniowy: tablicę, która ma pierwszy i ostatni indeks. Za każdym razem, gdy dodawany jest element, po prostu zwiększasz indeks ostatniego elementu, a gdy element jest usuwany, zwiększasz indeks pierwszego elementu. W obu przypadkach dodawanie jest wykonywane modulo rozmiar tablicy i upewnij się, że w razie potrzeby zwiększasz inny indeks, to znaczy, gdy kolejka jest pełna lub pusta.

Ponadto, jeśli jest to aplikacja typu routera, możesz również poeksperymentować z algorytmem, takim jak Random Early Dropping (RED), który losowo usuwa elementy z kolejki, zanim zostanie ona zapełniona. W niektórych przypadkach RED ma lepszą ogólną wydajność niż prosta metoda pozwalająca na zapełnienie kolejki przed upuszczeniem.


0

Właściwie możesz napisać swój własny plik Impl oparty na LinkedList, jest to całkiem proste, po prostu zastąp metodę add i wykonaj pięciolinię.


0

Myślę, że najbardziej pasująca odpowiedź pochodzi z tego innego pytania .

Apache commons collections 4 ma CircularFifoQueue, którego szukasz. Cytując javadoc:

CircularFifoQueue to kolejka pierwszy na wejściu o stałym rozmiarze, która zastępuje najstarszy element, jeśli jest pełny.


0

Proste rozwiązanie, poniżej znajduje się kolejka „Ciąg”

LinkedHashMap<Integer, String> queue;
int queueKeysCounter;

queue.put(queueKeysCounter++, "My String");
queueKeysCounter %= QUEUE_SIZE;

Zauważ, że nie zachowa to kolejności elementów w kolejce, ale zastąpi najstarszy wpis.


0

Zgodnie z zaleceniami w programach OOP, powinniśmy preferować kompozycję zamiast dziedziczenia

Oto moje rozwiązanie, mając to na uwadze.

package com.choiceview;

import java.util.ArrayDeque;

class Ideone {
    public static void main(String[] args) {
        LimitedArrayDeque<Integer> q = new LimitedArrayDeque<>(3);
        q.add(1);
        q.add(2);
        q.add(3);
        System.out.println(q);

        q.add(4);
        // First entry ie 1 got pushed out
        System.out.println(q);
    }
}

class LimitedArrayDeque<T> {

    private int maxSize;
    private ArrayDeque<T> queue;

    private LimitedArrayDeque() {

    }

    public LimitedArrayDeque(int maxSize) {
        this.maxSize = maxSize;
        queue = new ArrayDeque<T>(maxSize);
    }

    public void add(T t) {
        if (queue.size() == maxSize) {
            queue.removeFirst();
        }
        queue.add(t);
    }

    public boolean remove(T t) {
        return queue.remove(t);
    }

    public boolean contains(T t) {
        return queue.contains(t);
    }

    @Override
    public String toString() {
        return queue.toString();
    }
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.