คิวที่ จำกัด ขนาดที่มีองค์ประกอบ N สุดท้ายใน Java


198

คำถามที่ง่ายและรวดเร็วมากเกี่ยวกับห้องสมุด Java: มีคลาสสำเร็จรูปที่ใช้งานQueueกับขนาดสูงสุดคงที่ - คือมันอนุญาตให้เพิ่มองค์ประกอบได้เสมอ

แน่นอนว่ามันเป็นเรื่องเล็กน้อยที่จะใช้งานด้วยตนเอง:

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

เท่าที่ฉันเห็นไม่มีมาตรฐานการใช้งานใน Java stdlibs แต่อาจมีหนึ่งใน Apache Commons หรืออะไรแบบนั้น



9
@ เควิน: คุณหยอกล้อ
Mark Peters

5
Personnaly ฉันจะไม่แนะนำห้องสมุดอื่นหากเป็นเพียงการใช้ประโยชน์จากห้องสมุดนี้เท่านั้น ...
Nicolas Bousquet

2
@Override บูลีนสาธารณะเพิ่ม (PropagationTask t) {บูลีนเพิ่ม = super.add (t); ในขณะที่ (เพิ่ม && ขนาด ()> จำกัด ) {super.remove (); } เพิ่มผลตอบแทน; }
Renaud

6
คำเตือน:รหัสที่เป็นปัญหาถึงแม้ว่ามันจะใช้งานได้ แต่ก็สามารถย้อนกลับได้ มีวิธีการเพิ่มเติมที่สามารถเพิ่มองค์ประกอบเพิ่มเติมในคิว (เช่น addAll ()) ที่ไม่สนใจการตรวจสอบขนาดนี้ สำหรับรายละเอียดเพิ่มเติมโปรดดูJava รุ่นที่ 2 ที่มีประสิทธิภาพ - รายการที่ 16: การประพันธ์ที่โปรดปรานมากกว่าการสืบทอด
Diego

คำตอบ:


171

Apache คอมมอนส์คอลเลกชัน 4 มีCircularFifoQueue <>ซึ่งเป็นสิ่งที่คุณกำลังมองหา การอ้างถึง javadoc:

CircularFifoQueue เป็นคิวเข้าก่อนออกก่อนด้วยขนาดคงที่ที่แทนที่องค์ประกอบที่เก่าที่สุดหากเต็ม

    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 Commons Collection (3.x) เวอร์ชั่นเก่ากว่าคุณสามารถใช้CircularFifoBufferซึ่งเป็นสิ่งเดียวกันโดยไม่ใช้ยาสามัญ

อัปเดต : คำตอบที่อัปเดตแล้วหลังจากปล่อยคอมมอนส์คอลเล็กชั่นรุ่น 4 ที่รองรับชื่อสามัญ


1
เป็นผู้สมัครที่ดี แต่อนิจจามันไม่ได้ใช้ยาชื่อสามัญ :(
GreyCat

ขอบคุณ! หน้าตาว่าเป็นทางเลือกที่ทำงานได้มากที่สุดสำหรับตอนนี้ :)
GreyCat

3
ดูคำตอบอื่น ๆสำหรับลิงก์เพื่อEvictingQueueเพิ่มไปยังGoogle Guavaเวอร์ชัน 15 ประมาณ 2013-10
Basil Bourque

มีการเรียกกลับใด ๆ เมื่อองค์ประกอบถูกขับไล่ออกจากคิวเนื่องจากการเพิ่มคิวเต็ม?
ed22

"Circular Queue" เป็นเพียงการใช้งานเพียงอย่างเดียวที่ตอบสนองคำถามได้ แต่คำถามไม่ได้รับประโยชน์โดยตรงจากความแตกต่างที่สำคัญของคิวแบบวงกลมนั่นคือไม่ต้องฟรี / จัดสรรใหม่ที่ฝากข้อมูลแต่ละครั้งที่การเพิ่ม / ลบแต่ละครั้ง
simpleuser

90

ตอนนี้ Guava มีEvictingQueueซึ่งเป็นคิวที่ไม่มีการบล็อกซึ่งจะแสดงองค์ประกอบจากส่วนหัวของคิวโดยอัตโนมัติเมื่อพยายามที่จะเพิ่มองค์ประกอบใหม่ลงในคิวและเต็ม

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]

สิ่งนี้น่าสนใจที่จะใช้เมื่อมันออกมาอย่างเป็นทางการ
Asaf


1
อัปเดต:คลาสนี้เผยแพร่อย่างเป็นทางการกับ Google Guava ในเวอร์ชัน 15ประมาณ 2013-10
Basil Bourque

1
@MaciejMiklas คำถามที่ถามถึง FIFO EvictingQueueคือ FIFO ในกรณีที่มีข้อสงสัยให้ลองใช้โปรแกรมนี้: Queue<Integer> fifo = EvictingQueue.create(2); fifo.add(1); fifo.add(2); fifo.add(3); System.out.println(fifo); สังเกตผลลัพธ์:[2, 3]
kostmo

2
นี่คือคำตอบที่ถูกต้องแล้ว มันค่อนข้างชัดเจนจากเอกสาร แต่ EvictingQueue เป็น FIFO
Michael Böckling

11

ฉันชอบ @FractalizeR ทางออก แต่ฉันจะเก็บและคืนค่าจาก super.add (o) ด้วย!

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
เท่าที่ฉันเห็น FractalizeR ยังไม่ได้แก้ปัญหาใด ๆ เพียงแก้ไขคำถามเท่านั้น "การแก้ปัญหา" ภายในคำถามไม่ใช่วิธีแก้ปัญหาเนื่องจากคำถามเกี่ยวกับการใช้คลาสในไลบรารีมาตรฐานหรือกึ่งมาตรฐานไม่ใช่การทำของคุณเอง
GreyCat

3
ควรชี้ให้เห็นว่าวิธีนี้ไม่ปลอดภัยสำหรับเธรด
Konrad Morawski

7
@KonradMorawski คลาส LinkedList ทั้งหมดนั้นไม่ปลอดภัยสำหรับเธรดดังนั้นความคิดเห็นของคุณจึงไม่มีประโยชน์ในบริบทนี้!
Renaud

@RenaudBlue เธเธความปลอดภัยเป็นข้อกังวลที่ถูกต้อง (ถ้ามองข้ามบ่อย ๆ ) ดังนั้นฉันไม่คิดว่าความคิดเห็นนั้นไร้ประโยชน์ และเตือนว่าLinkedListไม่ปลอดภัยเธรดจะไม่มีจุดหมายอย่างใดอย่างหนึ่ง ในบริบทของคำถามนี้ข้อกำหนดเฉพาะของ OP ทำให้เป็นเรื่องสำคัญอย่างยิ่งที่การเพิ่มรายการจะดำเนินการเป็นการดำเนินการแบบอะตอมมิก กล่าวอีกนัยหนึ่งความเสี่ยงของการไม่มั่นใจอะตอมมิกจะมากกว่าในกรณีของ LinkedList ปกติ
Konrad Morawski

4
แยกทันทีที่มีคนโทรมาadd(int,E)แทน และไม่ว่าจะaddAllทำงานตามที่ตั้งใจหรือไม่ นั่นเป็นเหตุผลที่คุณควรเลือกคณะมากกว่าการสืบทอด…
Holger

6

ใช้การประพันธ์ไม่ขยาย (ใช่ฉันหมายถึงการขยายตามที่อ้างอิงถึงคำสำคัญการขยายใน java และใช่นี่คือการสืบทอด) การเขียนเรียงความนั้นยิ่งใหญ่กว่าเพราะมันป้องกันการติดตั้งของคุณอย่างสมบูรณ์ทำให้คุณสามารถเปลี่ยนการใช้งานโดยไม่ส่งผลกระทบต่อผู้ใช้ในชั้นเรียน

ฉันแนะนำให้ลองทำสิ่งนี้ (ฉันกำลังพิมพ์โดยตรงในหน้าต่างนี้ดังนั้นผู้ซื้อระวังข้อผิดพลาดทางไวยากรณ์):

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

ตัวเลือกที่ดีกว่า (ขึ้นอยู่กับคำตอบของ Asaf) อาจจะห่อApache Collections CircularFifoBufferด้วยคลาสทั่วไป ตัวอย่างเช่น:

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 ถ้าคุณอธิบายว่าทำไมการจัดองค์ประกอบเป็นตัวเลือกที่ดีกว่า (นอกเหนือจาก "ชอบการแต่งมากกว่าการสืบทอด) ... และมีเหตุผลที่ดีมาก
kdgregory

1
การจัดองค์ประกอบเป็นตัวเลือกที่ไม่ดีสำหรับงานของฉันที่นี่: มันหมายถึงอย่างน้อยสองเท่าของจำนวนวัตถุ => อย่างน้อยสองครั้งบ่อยครั้งที่การรวบรวมขยะ ฉันใช้คิวจำนวน จำกัด ขนาดใหญ่ (หลายสิบล้าน) เช่นนี้: แผนที่ <Long, LimitedSizeQueue <String>>
GreyCat

@ GreyCat - ฉันเข้าใจคุณยังไม่ได้ดูวิธีLinkedListการใช้งานแล้ว วัตถุพิเศษที่สร้างขึ้นเป็น wrapper รอบ ๆ รายการจะค่อนข้างเล็กแม้ว่าจะมีหลายสิบล้านครั้งก็ตาม
kdgregory

ฉันกำลังจะ "ลดขนาดของอินเทอร์เฟซ" แต่ "ป้องกันการติดตั้ง" นั้นก็เหมือนกันมาก ทั้งตอบคำร้องเรียนของMark Peterเกี่ยวกับแนวทางของ OP
kdgregory

4

สิ่งเดียวที่ฉันรู้ว่ามีพื้นที่ จำกัด คืออินเตอร์เฟส BlockingQueue (ซึ่งถูกใช้งานโดยคลาส ArrayBlockingQueue) - แต่พวกเขาไม่ได้ลบองค์ประกอบแรกถ้าเติม แต่แทนที่จะบล็อกการดำเนินการวางจนกว่าพื้นที่ว่าง (ลบออกโดยเธรดอื่น )

สำหรับความรู้ของฉันการใช้งานเล็กน้อยของคุณเป็นวิธีที่ง่ายที่สุดในการรับพฤติกรรมดังกล่าว


ฉันเรียกดูแล้วผ่านคลาส Java stdlib และน่าเศร้า BlockingQueueไม่ใช่คำตอบ ฉันเคยนึกถึงห้องสมุดทั่วไปอื่น ๆ เช่น Apache Commons ห้องสมุดของ Eclipse Spring's ส่วนเพิ่มเติมของ Google เป็นต้น
GreyCat

3

คุณสามารถใช้MinMaxPriorityQueueจากGoogle Guavaจาก javadoc:

คิวลำดับความสำคัญต่ำสุดสามารถกำหนดค่าด้วยขนาดสูงสุด หากเป็นเช่นนั้นในแต่ละครั้งที่ขนาดของคิวเกินกว่าค่านั้นคิวจะลบองค์ประกอบที่ยิ่งใหญ่ที่สุดโดยอัตโนมัติตามตัวเปรียบเทียบ (ซึ่งอาจเป็นองค์ประกอบที่เพิ่งเพิ่มเข้าไป) สิ่งนี้แตกต่างจากคิวแบบมีขอบทั่วไปซึ่งบล็อกหรือปฏิเสธองค์ประกอบใหม่เมื่อเต็ม


3
คุณเข้าใจว่าคิวลำดับความสำคัญคืออะไรและแตกต่างจากตัวอย่างของ OP หรือไม่
kdgregory

2
@ Mark Peters - ฉันแค่ไม่รู้จะพูดอะไร แน่นอนว่าคุณสามารถสร้างลำดับความสำคัญของคิวได้เช่นเดียวกับคิวต่อเนื่อง นอกจากนี้คุณยังสามารถทำให้ประพฤติเช่นMap Listแต่แนวคิดทั้งสองแสดงความเข้าใจที่ไม่สมบูรณ์ของอัลกอริทึมและการออกแบบซอฟต์แวร์
kdgregory

2
@ Mark Peters - ไม่ใช่คำถามทุกข้อเกี่ยวกับ SO เกี่ยวกับวิธีที่ดีในการทำบางสิ่ง?
jtahlborn

3
@ jtahlborn: ไม่ชัดเจน (รหัสกอล์ฟ) แต่ถึงแม้ว่าพวกเขาดีไม่ได้เป็นเกณฑ์ขาวดำ สำหรับโครงการหนึ่งอาจหมายถึง "มีประสิทธิภาพมากที่สุด" สำหรับโครงการอื่นอาจหมายถึง "ง่ายที่สุดในการรักษา" และอีกโครงการหนึ่งอาจหมายถึง "โค้ดจำนวนน้อยที่สุดกับไลบรารีที่มีอยู่" ทุกสิ่งที่ไม่เกี่ยวข้องเพราะผมไม่เคยบอกว่านี้เป็นดีคำตอบ ฉันแค่บอกว่ามันจะเป็นทางออกโดยไม่ต้องใช้ความพยายามมากเกินไป เปลี่ยนเป็นMinMaxPriorityQueueสิ่งที่ OP ต้องการมีความสำคัญมากกว่าการแก้ไข a LinkedList(รหัสของ OP ไม่ได้เข้ามาใกล้)
Mark Peters

3
บางทีพวกคุณกำลังตรวจสอบคำที่ฉันเลือก "ในทางปฏิบัติที่เกือบจะเพียงพอแล้ว" ฉันไม่ได้หมายความว่าวิธีนี้จะเพียงพอสำหรับปัญหาของ OP หรือโดยทั่วไป ฉันอ้างถึงตัวเลือกของการลดระดับลงlongเป็นประเภทเคอร์เซอร์ภายในคำแนะนำของฉันเองโดยบอกว่ามันจะมีความกว้างเพียงพอในทางปฏิบัติแม้ว่าในทางทฤษฎีแล้วคุณสามารถเพิ่มวัตถุมากกว่า 2 ^ 64 ลงในคิวนี้ได้ .
Mark Peters

3

LRUMap เป็นไปได้อีกอย่างหนึ่งจาก Apache Commons

http://commons.apache.org/collections/apidocs/org/apache/commons/collections/map/LRUMap.html


ฉันไม่เข้าใจวิธีการปรับ LRUMap ให้เป็นคิวและฉันคิดว่ามันจะค่อนข้างยากที่จะใช้แม้ว่ามันจะเป็นไปได้
GreyCat

-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
คำถามคือเกี่ยวกับคลาสในไลบรารีคลาสคอลเลกชันที่ได้รับความนิยมไม่ใช่ "การแก้ปัญหา" homebrew แบบเรียบง่ายของตัวเอง
GreyCat

2
ไม่สำคัญว่า google จะพบหน้านี้ในแบบสอบถามอื่น =)
user590444

1
คำตอบนี้ปรากฏในคิวตรวจสอบคุณภาพต่ำเนื่องจากคุณไม่ได้ให้คำอธิบายใด ๆ ของรหัส หากรหัสนี้ตอบคำถามให้พิจารณาเพิ่มการเพิ่มข้อความอธิบายรหัสในคำตอบของคุณ ด้วยวิธีนี้คุณมีแนวโน้มที่จะได้รับ upvotes มากขึ้นและช่วยให้ผู้ถามเรียนรู้สิ่งใหม่
lmo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.