Чи існує фіксований розмір черги, який видаляє зайві елементи?


128

Мені потрібна черга з фіксованим розміром. Коли я додаю елемент і черга заповнена, він повинен автоматично видалити найстаріший елемент.

Чи існує реалізація для цього на Java?


Відповіді:


19

Немає існуючої реалізації в мові та режимі виконання Java. Усі черги розширюють AbstractQueue , і її документ чітко зазначає, що додавання елемента до повної черги завжди закінчується винятком. Найкраще було б (і досить просто) загорнути Чергу в свій власний клас, щоб мати необхідну функціональність.

Ще раз, оскільки всі черги є дітьми AbstractQueue, просто використовуйте це як свій внутрішній тип даних, і у вас повинна бути гнучка реалізація, яка працює практично за короткий час :-)

ОНОВЛЕННЯ:

Як зазначено нижче, доступні дві відкриті реалізації (ця відповідь досить стара, люди!), Детальну інформацію див. У цій відповіді .


4
Використовуйте чергу замість AbstractQueue ... можуть бути черги, які реалізують інтерфейс, але не розширюють абстрактний клас.
TofuBeer

1
У Python ви можете використовувати a collection.dequeіз зазначеним maxlen.
Йонас Грьогер

2
ОНОВЛЕННЯ Зараз є два таких класи. Не потрібно писати своє. Дивіться мою відповідь на цій сторінці.
Василь Бурк

108

Насправді LinkedHashMap робить саме те, що ви хочете. Вам потрібно перекрити removeEldestEntryметод.

Приклад для черги з максимум 10 елементами:

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

Якщо "RemoveEldestEntry" повертає значення true, найстарший запис видаляється з карти.


7
це насправді не робить те, що робить черга, як я можу отримати найновіший. об’єкт?
Олексій

69

Так, два

З власного дублюючого запитання з цією правильною відповіддю я дізнався про два:


Я продуктивно використовував гуаву EvictingQueue , працював добре.

Для отримання екземпляра EvictingQueueвиклику статичним заводським методом createта вкажіть максимальний розмір.

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

... а якщо ви не можете використовувати Commons Collection 4.0, то CircularFifoBuffer здається схожим на CircularFifoQueue в v 3.0.
Шрідхар Сарнобат

CircularFifoQueueпосилання мертве, замість цього використовуйте commons.apache.org/proper/commons-collections/apidocs/org/…
користувач7294900

@ user7294900 Спасибі, посилання виправлено. FYI, Переповнення стека пропонує вам внести такі зміни безпосередньо у відповідь самостійно. Редагувати може будь-хто, а не лише оригінальний автор. Переповнення стека має на меті більше схожий на Вікіпедію.
Василь Бурк

@BasilBourque так, але такі зміни можна відхилити навіть я при зміні посилань - це сіра зона
user7294900

18

Я просто реалізував чергу фіксованого розміру таким чином:

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
Це повинно бутиremoveRange(0, size() - maxSize)
Ахмед Гегазі

@AhmedHegazy removeRange (0, розмір () - maxSize - 1) є правильним
Ashish Doneriya

Я згоден з Amhed вище. Видаліть - 1. В іншому випадку при максимальній потужності ви отримаєте масив maxSize + 1, оскільки ми говоримо про 0. Наприклад. Якщо maxSize = 50, то при додаванні нового об'єкта формула RemoveRange в початковому дописі буде 51 - 50 - 1 = 0 (тобто нічого не видалено).
Етеп

8

Це те, що я зробив із Queueзагорнутою LinkedList, це розмір фіксованого розміру, який я надаю ось 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

Я думаю, що ти описуєш - кругову чергу. Ось приклад , і ось краще один


4

Цей клас виконує завдання, використовуючи композицію замість успадкування (інші відповіді тут), яка виключає можливість певних побічних ефектів (як охоплює Джош Блох у Essential Java). Обрізання базового LinkedList відбувається за методами додавання, додавання та пропозиції.

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);
    }
}

Використання та результат тесту:

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

Звучить як звичайний Список, де метод додавання містить додатковий фрагмент, який скорочує список, якщо він занадто довгий.

Якщо це занадто просто, вам, ймовірно, потрібно відредагувати опис проблеми.


Насправді йому потрібно буде видалити перший елемент (тобто найраніший), обрізання видалить останній елемент. Досі практичний із LinkedList.
MAK


0

Не зовсім зрозуміло, які вимоги ви зумовили, щоб ви поставили це питання. Якщо вам потрібна структура даних фіксованого розміру, ви також можете переглянути різні політики кешування. Однак, оскільки у вас є черга, я найкраще здогадуюсь, що ви шукаєте певний тип функціональності маршрутизатора. У такому випадку я б пішов з буфером кільця: масивом, який має перший і останній індекс. Щоразу, коли елемент додається, ви просто збільшуєте індекс останнього елемента, а коли елемент видаляєте, збільшуйте індекс першого елемента. В обох випадках додавання виконується за розміром масиву і обов'язково збільшується інший індекс, коли це необхідно, тобто коли черга повна або порожня.

Крім того, якщо це програма типу маршрутизатора, ви також можете поекспериментувати з таким алгоритмом, як Random Early Dropping (RED), який викидає елементи з черги випадковим чином ще до того, як він заповниться. В деяких випадках було виявлено, що RED має кращі показники загальної продуктивності, ніж простий метод, що дозволяє черзі заповнюватись, перш ніж випадати.


0

Насправді ви можете написати власний імпульс на основі LinkedList, це досить прямо, просто перемотайте метод додавання та робіть персонал.


0

Я думаю, що найкраща відповідь - це саме з цього питання .

Колекція Apache commons 4 має CircularFifoQueue, яку саме ви шукаєте. Цитуючи javadoc:

CircularFifoQueue - це черга, що перебуває на першому місці, із фіксованим розміром, яка замінює її найстаріший елемент, якщо він заповнений.


0

Просте рішення, нижче - черга "Рядок"

LinkedHashMap<Integer, String> queue;
int queueKeysCounter;

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

Зауважте, що це не підтримує порядок елементів у черзі, але це замінить найдавніший запис.


0

Як рекомендується в ООП, ми повинні віддавати перевагу складу над спадщиною

Тут моє рішення, враховуючи це.

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();
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.