Java에서 마지막 N 개 요소를 보유하는 크기 제한 큐


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 stdlib에는 표준 구현이 없지만 Apache Commons 또는 이와 유사한 것이 있습니까?



9
@ 케빈 : 당신은 정말 애타게입니다.
— Mark Peters

5
이 도서관이 유일한 도서관이라면 개인적으로 다른 도서관을 소개하지 않겠습니다 ...
— Nicolas Bousquet

2
@Override public boolean add (PropagationTask t) {boolean added = super.add (t); while (추가 된 && size ()> limit) {super.remove (); } 반품 추가; }
— Renaud

6
경고 : 문제 의 코드는 분명히 작동하지만 역효과를 낼 수 있습니다. 이 크기 검사를 무시하는 addAll () 등의 요소를 대기열에 추가 할 수있는 추가 메소드가 있습니다. 자세한 내용은 Effective Java 2nd Edition-항목 16 : 상속 선호 구성
— Diego

답변:


171

Apache Commons Collections 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 Collections (3.x)를 사용 하는 경우 기본적으로 제네릭없이 동일한 CircularFifoBuffer 를 사용할 수 있습니다 .

업데이트 : 일반을 지원하는 Commons Collections 버전 4 릴리스 이후의 답변이 업데이트되었습니다.


1
좋은 후보이지만 아아, 제네릭을 사용하지 않습니다 :(
— GreyCat

감사! 그것이 현재 가장 실용적인 대안 인 것
— 같습니다

3
2013-10 년경 Google Guava 버전 15 에 추가 된 링크에 대해서는 이 다른 답변 을 참조하십시오 . EvictingQueue
— Basil Bourque

전체 대기열에 추가하여 대기열에서 요소가 제거 될 때 호출되는 콜백이 있습니까?
— ed22

"원형 대기열"은 질문을 만족시키는 하나의 구현 일뿐입니다. 그러나이 질문은 순환 대기열의 주요 차이점, 즉 각 추가 / 삭제시 각 버킷을 해제 / 재 할당 할 필요가 없다는 직접적인 이점을 얻지 못합니다.
— simpleuser

90

구아바 이제 갖는다 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

출처는 다음과 같습니다. code.google.com/p/guava-libraries/source/browse/guava/src/com/…- 현재 구아바 버전으로 복사하고 컴파일하는 것이 쉬운 것 같습니다
— Tom Carchrae

1
업데이트 : 이 수업은 2013 년 10 월경 Google Guava 15 버전 으로 공식 출시되었습니다 .
— Basil Bourque

1
@MaciejMiklas FIFO를 묻는 질문은 EvictingQueueFIFO입니다. 의심이가는 경우 다음 프로그램을 시도해보십시오 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

composition not extends를 사용하십시오 (예, java의 extends 키워드에 대한 참조에서와 같이 extends를 의미하며 이것이 상속입니다). 컴포지션은 구현을 완전히 차단하여 클래스 사용자에게 영향을주지 않고 구현을 변경할 수 있기 때문에 더욱 우수합니다.

나는 이런 식으로 시도하는 것이 좋습니다 (이 창에 직접 입력하고 있으므로 구매자는 구문 오류에주의하십시오) :

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
컴포지션은 여기서 내 작업에 적합하지 않은 선택입니다. 개체 수의 두 배 이상 => 가비지 수집이 두 번 이상 더 자주 발생 함을 의미합니다. Map <Long, LimitedSizeQueue <String >>과 같은 제한된 크기의 대기열을 대량으로 사용합니다.
— GreyCat

@GreyCat-그렇다면 LinkedList구현 방법을 보지 못했습니다 . 목록 주위에 래퍼로 ​​생성 된 추가 개체는 "수백만"인스턴스의 경우에도 아주 작습니다.
— kdgregory

나는 "인터페이스의 크기를 줄인다"고했지만 "구현을 막는다"는 것은 거의 같은 일이다. OP의 접근 방식에 대한 Mark Peter 의 불만에 대한 답변 입니다.
— kdgregory

4

제한된 공간을 가지고 있다는 것을 알고있는 유일한 것은 BlockingQueue 인터페이스 (예 : ArrayBlockingQueue 클래스에 의해 구현 됨)입니다. 그러나 채워지면 첫 번째 요소를 제거하지 않고 대신 공간이 비워 질 때까지 put 작업을 차단합니다 (다른 스레드에 의해 제거됨) ).

내 지식으로는 사소한 구현이 그러한 행동을 얻는 가장 쉬운 방법입니다.


이미 Java stdlib 클래스를 탐색했으며 슬프게도 BlockingQueue대답이 아닙니다. Apache Commons, Eclipse 라이브러리, Spring, Google 추가 등의 다른 공통 라이브러리를 생각 했습니까?
— GreyCat

3

javadoc 에서 Google Guava 의 MinMaxPriorityQueue 를 사용할 수 있습니다 .

최소-최대 우선 순위 큐는 최대 크기로 구성 할 수 있습니다. 그렇다면 대기열의 크기가 해당 값을 초과 할 때마다 대기열은 비교기 (방금 추가 된 요소 일 수 있음)에 따라 가장 큰 요소를 자동으로 제거합니다. 이는 새 요소가 가득 차면 차단하거나 거부하는 기존의 제한 대기열과 다릅니다.


3
우선 순위 대기열이 무엇이며 OP의 예와 어떻게 다른지 이해하십니까?
— kdgregory

2
@ Mark Peters-나는 단지 무엇을 말할지 모른다. 물론 우선 순위 대기열을 fifo 대기열처럼 작동시킬 수 있습니다. 당신은 또한 Map같은 행동을 할 수 있습니다 List. 그러나 두 아이디어는 알고리즘과 소프트웨어 디자인을 완전히 이해하지 못합니다.
— kdgregory

2
@ Mark Peters- 뭔가를 하는 좋은 방법에 대한 모든 질문 이 아니 십니까?
— jtahlborn

3
@ jtahlborn : 분명히 (코드 골프)는 아니지만 그들이 좋더라도 흑백 기준이 아닙니다. 특정 프로젝트의 경우 good은 "가장 효율적"을 의미하고, 다른 프로젝트는 "유지하기가 가장 쉬움"을 의미하고 또 다른 프로젝트는 "기존 라이브러리에 가장 적은 양의 코드"를 의미 할 수 있습니다. 나는이 말을 한 적이 있기 때문에 관계가 그 모든 했다 좋은의 대답. 방금 너무 많은 노력없이 솔루션이 될 수 있다고 말했습니다. MinMaxPriorityQueueOP가 원하는 것으로 바꾸는 것은 A 를 수정하는 것보다 사소한 것입니다 LinkedList(OP의 코드는 가깝지 않습니다).
— Mark Peters

3
어쩌면 너희들은 "실제로 충분할 것이다"라는 단어의 선택을 검토하고있을 것이다. 나는이 솔루션이 OP의 문제 나 일반적으로 충분할 것이라는 것을 의미하지는 않았다. long이론적으로 솔루션이 무너지는 지점에 2 ^ 64 개 이상의 객체를이 대기열에 추가 할 수는 있지만 실제로 실제로 충분히 넓을 것이라고 말하는 내 자신의 제안 내에서 커서 유형 으로 내림차순 선택을 언급하고 있었습니다. .
— 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
문제는 인기있는 컬렉션 클래스 라이브러리의 클래스에 관한 것이지, 자체적으로 롤링하는 것이 아니라 최소한의 홈 브루 "솔루션"이 이미 제공되었습니다.
— GreyCat

2
그 문제) = 다른 쿼리에이 페이지를 찾을 구글하지 않습니다
— user590444

1
이 답변은 코드에 대한 설명을 제공하지 않기 때문에 품질이 낮은 검토 대기열에 나타납니다. 이 코드가 질문에 대한 답변이면 코드를 설명하는 텍스트를 추가하여 추가하십시오. 이렇게하면 더 많은 투표를 할 가능성이 높아지고 질문자가 새로운 것을 배우도록 도울 수 있습니다.
— lmo
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.