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 GuavaMinMaxPriorityQueue 를 사용할 수 있습니다 .

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


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.