Java'da son N öğeyi barındıran boyut sınırlı kuyruk


198

Java kitaplıkları hakkında çok basit ve hızlı bir soru: Queuesabit bir maksimum boyutta bir uygulayan hazır bir sınıf var mı?

Elbette, manuel olarak uygulanması önemsizdir:

import java.util.LinkedList;

public class LimitedQueue<E> extends LinkedList<E> {
    private int limit;

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

    @Override
    public boolean add(E o) {
        super.add(o);
        while (size() > limit) { super.remove(); }
        return true;
    }
}

Gördüğüm kadarıyla, Java stdlibs'te standart bir uygulama yok, ancak Apache Commons'ta bir tane veya böyle bir şey olabilir mi?



9
@Kevin: Sen çok alay ediyorsun.
Mark Peters

5
Personnaly Bu kütüphanenin tek kullanımı olsaydı başka bir kütüphane tanıtmayacağım ...
Nicolas Bousquet

2
@ Herkese açık boole eklentisini geçersiz kılma (PropagationTask t) {boolean added = super.add (t); while (&& size ()> limit) eklendi {super.remove (); } geri dönüş eklendi; }
Renaud

6
Uyarı: söz konusu kod, görünüşe göre çalışır, ancak geri tepebilir. Kuyruğa bu boyut denetimini yok sayan daha fazla öğe (addAll () gibi) ekleyebilecek ek yöntemler vardır. Daha fazla ayrıntı için bkz. Etkili Java 2. Baskı - Madde 16: Miras üzerinde kompozisyon oluşturma
Diego

Yanıtlar:


171

Apache ortak koleksiyonları 4, aradığınız şey olan bir CircularFifoQueue <> değerine sahiptir . Javadoc'tan alıntı:

CircularFifoQueue, doluysa en eski öğesini değiştiren sabit boyutlu ilk giren ilk çıkar kuyruğudur.

    import java.util.Queue;
    import org.apache.commons.collections4.queue.CircularFifoQueue;

    Queue<Integer> fifo = new CircularFifoQueue<Integer>(2);
    fifo.add(1);
    fifo.add(2);
    fifo.add(3);
    System.out.println(fifo);

    // Observe the result: 
    // [2, 3]

Apache ortak koleksiyonlarının (3.x) daha eski bir sürümünü kullanıyorsanız , temel olarak jenerik olmayan aynı şey olan CircularFifoBuffer'ı kullanabilirsiniz .

Güncelleme : müşterek koleksiyonları jenerikleri destekleyen sürüm 4'ün yayınlanmasının ardından güncellenen cevap.


1
Bu iyi bir aday, ama, ne yazık ki, jenerik kullanmıyor :(
GreyCat

Teşekkürler! Şimdilik en uygun alternatif bu görünüyor :)
GreyCat

3
2013-10 civarında Google Guava sürüm 15'e eklenen bağlantı için bu diğer yanıta bakın . EvictingQueue
Basil Bourque

Tam kuyruğa ekleme nedeniyle öğe kuyruktan çıkarıldığında çağrılan herhangi bir geri arama var mı?
ed22

"Dairesel Kuyruk", yalnızca soruyu karşılayan bir uygulamadır. Ancak soru, dairesel bir kuyruğun ana farklılıklarından doğrudan faydalanmamaktadır, yani her toplama / silme işleminde her bir kovayı serbest bırakmak / yeniden tahsis etmek zorunda değildir.
simpleuser

90

Guava'nın artık kuyruğa yeni öğeler eklemeye çalışırken öğeleri otomatik olarak kuyruğun başından tahliye eden , engellenmeyen bir kuyruk olan bir EvictingQueue var ve dolu.

import java.util.Queue;
import com.google.common.collect.EvictingQueue;

Queue<Integer> fifo = EvictingQueue.create(2); 
fifo.add(1); 
fifo.add(2); 
fifo.add(3); 
System.out.println(fifo); 

// Observe the result: 
// [2, 3]

Bu resmi olarak ortaya çıktığında kullanmak ilginç olmalıdır.
Asaf


1
Güncelleme: Bu sınıf 2013-10 civarında Google Guava ile sürüm 15'te resmen yayınlandı .
Basil Bourque

1
@MaciejMiklas Soru bir FIFO istiyor EvictingQueue, bir FIFO. Herhangi bir şüphe durumunda, bu programı deneyin Queue<Integer> fifo = EvictingQueue.create(2); fifo.add(1); fifo.add(2); fifo.add(3); System.out.println(fifo); : [2, 3]
Sonucu

2
Şimdi doğru cevap bu. Belgelerinden biraz belirsiz, ancak EvictingQueue bir FIFO.
Michael Böckling

11

@FraktalizeR çözümünü seviyorum. Ama ek olarak tutmak ve super.add (o) değerini iade ediyorum!

public class LimitedQueue<E> extends LinkedList<E> {

    private int limit;

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

    @Override
    public boolean add(E o) {
        boolean added = super.add(o);
        while (added && size() > limit) {
           super.remove();
        }
        return added;
    }
}

1
Görebildiğim kadarıyla FractalizeR herhangi bir çözüm sunmadı, sadece soruyu düzenledi. Soru içindeki "çözüm" bir çözüm değildir , çünkü soru kendi sınıfınızı değil, standart veya yarı standart kütüphanede bir sınıf kullanmakla ilgilidir.
13:13, GreyCat

3
Bu çözümün iplik güvenli olmadığı belirtilmelidir
Konrad Morawski

7
@KonradMorawski LinkedList sınıfının tamamı zaten iş parçacığı için güvenli değildir, bu nedenle yorumunuz bu bağlamda anlamsızdır!
Renaud

@RenaudBlue iplik güvenliği (genellikle göz ardı edilirse) geçerli bir endişe, bu yüzden yorum anlamsız olduğunu sanmıyorum. ve LinkedListiş parçacığı güvenli olmadığını hatırlatmak da anlamsız olurdu. bu soru bağlamında OP'nin özel gereksinimi, bir öğe eklemenin atomik işlem olarak gerçekleştirilmesini özellikle önemli kılmaktadır. diğer bir deyişle, atomisiteyi sağlamayan risk, düzenli bir LinkedList durumunda olduğundan daha büyük olacaktır.
Konrad Morawski

4
Birisi aradığı anda add(int,E)kırılır. Ve addAllamaçlandığı gibi çalışıp çalışmadığı, belirtilmemiş uygulama ayrıntılarına bağlıdır. Bu yüzden miras yerine heyeti tercih etmelisiniz…
Holger

6

Genişlemeyen kompozisyon kullanın (evet, java'daki extends anahtar kelimesine yapılan bir başvuruda olduğu gibi, extends demekti ve evet bu kalıtımdır). Kompozisyon daha üst düzeydir, çünkü uygulamanızı tamamen korur ve sınıfınızın kullanıcılarını etkilemeden uygulamayı değiştirmenize izin verir.

Böyle bir şey denemenizi öneririz (doğrudan bu pencereye yazıyorum, bu yüzden alıcı sözdizimi hatalarına dikkat edin):

public LimitedSizeQueue implements Queue
{
  private int maxSize;
  private LinkedList storageArea;

  public LimitedSizeQueue(final int maxSize)
  {
    this.maxSize = maxSize;
    storageArea = new LinkedList();
  }

  public boolean offer(ElementType element)
  {
    if (storageArea.size() < maxSize)
    {
      storageArea.addFirst(element);
    }
    else
    {
      ... remove last element;
      storageArea.addFirst(element);
    }
  }

  ... the rest of this class

Daha iyi bir seçenek (Asaf'ın cevabına göre) Apache Collections CircularFifoBuffer'ı genel bir sınıfla sarmak olabilir . Örneğin:

public LimitedSizeQueue<ElementType> implements Queue<ElementType>
{
    private int maxSize;
    private CircularFifoBuffer storageArea;

    public LimitedSizeQueue(final int maxSize)
    {
        if (maxSize > 0)
        {
            this.maxSize = maxSize;
            storateArea = new CircularFifoBuffer(maxSize);
        }
        else
        {
            throw new IllegalArgumentException("blah blah blah");
        }
    }

    ... implement the Queue interface using the CircularFifoBuffer class
}

2
1 eğer sen kompozisyon (miras üzerinde kompozisyon tercih" haricinde) daha iyi bir seçimdir niçin ... ve çok iyi bir neden yoktur
kdgregory

1
Kompozisyon buradaki görevim için kötü bir seçimdir: nesne sayısının en az iki katı => en az iki kez daha sık çöp toplama anlamına gelir. Bu sınırlı boyutlu kuyrukların büyük miktarlarını (on milyonlarca) şu şekilde kullanıyorum: Map <Long, LimitedSizeQueue <String>>.
GreyCat

@GreyCat - Anlıyorum, nasıl LinkedListuygulandığına bakmadınız . Listenin etrafına sarıcı olarak yaratılan ekstra nesne, "on milyonlarca" örneğiyle bile oldukça küçük olacaktır.
kdgregory

Ben "arayüzün boyutunu küçültmek" için gidiyordum, ama "uygulama kalkanları" hemen hemen aynı şey. Her ikisi de Mark Peter'ın OP'nin yaklaşımı hakkındaki şikayetlerini yanıtlıyor .
kdgregory

4

Sınırlı alanı olduğunu bildiğim tek şey BlockingQueue arabirimi (örneğin ArrayBlockingQueue sınıfı tarafından uygulanır) - ancak doldurulduklarında ilk öğeyi kaldırmazlar, ancak boşluk boş olana kadar put işlemini engellerler (diğer iş parçacıkları tarafından kaldırılırlar) ).

Bildiğim kadarıyla, önemsiz uygulamanız böyle bir davranışa ulaşmanın en kolay yoludur.


Java stdlib sınıflarına zaten göz attım ve ne yazık ki BlockingQueuebir cevap değil. Apache Commons, Eclipse'in kütüphaneleri, Spring'ler, Google'ın eklemeleri gibi diğer yaygın kütüphaneleri düşündüm.
GreyCat

3

Google Guava'dan , javadoc'tan bir MinMaxPriorityQueue kullanabilirsiniz :

Bir min-maks öncelik sırası maksimum boyutta yapılandırılabilir. Öyleyse, sıranın boyutu bu değeri her aştığında, kuyruk karşılaştırıcısına göre (en yeni eklenen öğe olabilir) en büyük öğesini otomatik olarak kaldırır. Bu, dolduğunda yeni öğeleri engelleyen veya reddeden geleneksel sınırlı kuyruklardan farklıdır.


3
Öncelik sırasının ne olduğunu ve OP'nin örneğinden nasıl farklı olduğunu anlıyor musunuz?
kdgregory

2
@Mark Peters - Ne diyeceğimi bilmiyorum. Elbette, öncelik sırasının fifo kuyruğu gibi davranmasını sağlayabilirsiniz. Ayrıca Mapgibi davranabilirsiniz List. Ancak her iki fikir de algoritmaların ve yazılım tasarımının tamamen anlaşıldığını gösterir.
kdgregory

2
@Mark Peters - SO'daki her soru bir şey yapmanın iyi bir yolu hakkında değil mi?
jtahlborn

3
@jtahlborn: Açıkça değil (kod golf), ama olsalar bile, iyi bir siyah beyaz kriter değildir. Belirli bir proje için, iyi "en verimli" anlamına gelebilir, bir diğeri için "bakımı en kolay" anlamına gelebilir ve bir diğeri için "mevcut kütüphanelerle en az kod miktarı" anlamına gelebilir. Ben bunu söylemedim çünkü alakasız olduğunu tüm idi bir iyi cevap. Az önce çok fazla çaba harcamadan bir çözüm olabileceğini söyledim. Bir Torna MinMaxPriorityQueueOP istediğini içine değiştirerek daha önemsiz olduğu LinkedList(hatta yakın gelmiyor OP'ın kodu).
Mark Peters

3
Belki siz, "neredeyse kesinlikle yeterli olacak pratikte" kelime seçimimi inceliyorsunuzdur. Bu çözümün OP'nin sorunu için veya genel olarak neredeyse kesinlikle yeterli olacağı anlamına gelmiyordum. longKendi önerim içinde bir imleç türü olarak azalan bir seçimden bahsediyordum, teorik olarak çözümün bozulacağı bu sıraya 2 ^ 64'den fazla nesne ekleyebilseniz bile pratikte yeterince geniş olacağını söyliyordum. .
Mark Peters


-2
    public class ArrayLimitedQueue<E> extends ArrayDeque<E> {

    private int limit;

    public ArrayLimitedQueue(int limit) {
        super(limit + 1);
        this.limit = limit;
    }

    @Override
    public boolean add(E o) {
        boolean added = super.add(o);
        while (added && size() > limit) {
            super.remove();
        }
        return added;
    }

    @Override
    public void addLast(E e) {
        super.addLast(e);
        while (size() > limit) {
            super.removeLast();
        }
    }

    @Override
    public boolean offerLast(E e) {
        boolean added = super.offerLast(e);
        while (added && size() > limit) {
            super.pollLast();
        }
        return added;
    }
}

3
Soru, popüler koleksiyon sınıf kütüphanelerindeki sınıflarla ilgiliydi, kişinin kendi haddeleme değil - minimalist homebrew "çözümü" zaten söz konusuydu.
GreyCat


1
Bu yanıt, muhtemelen kod hakkında herhangi bir açıklama yapmadığınız için düşük kaliteli inceleme sırasında ortaya çıktı. Bu kod soruyu cevaplıyorsa, cevabınızdaki kodu açıklayan bir metin eklemeyi düşünün. Bu şekilde, daha fazla oy almanız ve ankete katılan kişinin yeni bir şey öğrenmesine yardımcı olmanız çok daha olasıdır.
lmo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.