[java] 한 줄로 스트림 / 목록의 마지막 요소 가져 오기

다음 코드에서 스트림 또는 목록의 마지막 요소를 어떻게 얻을 수 있습니까?

어디 data.careasA는 List<CArea>:

CArea first = data.careas.stream()
                  .filter(c -> c.bbox.orientationHorizontal).findFirst().get();

CArea last = data.careas.stream()
                 .filter(c -> c.bbox.orientationHorizontal)
                 .collect(Collectors.toList()).; //how to?

보시다시피 첫 번째 요소를 얻는 filter것은 어렵지 않습니다.

그러나 한 줄의 마지막 요소를 얻는 것은 진정한 고통입니다.

  • 에서 직접 얻을 수없는 것 같습니다 Stream. (유한 스트림에서만 의미가 있습니다)
  • 또한 당신 같은 것들을 얻을 수없는 것 같습니다 first()last()로부터 List정말 고통 인터페이스를.

인터페이스의 요소가 정렬되어 있고 크기가 더 알려져 있기 때문에 인터페이스 에서 first()last()메서드를 제공하지 않는다는 주장은 보이지 않습니다 List.

그러나 원래 답변에 따르면 유한의 마지막 요소를 얻는 방법은 Stream무엇입니까?

개인적으로 이것은 내가 얻을 수있는 가장 가까운 것입니다.

int lastIndex = data.careas.stream()
        .filter(c -> c.bbox.orientationHorizontal)
        .mapToInt(c -> data.careas.indexOf(c)).max().getAsInt();
CArea last = data.careas.get(lastIndex);

그러나 indexOf모든 요소에를 사용하는 것이 포함되며 성능을 저하시킬 수 있으므로 일반적으로 원하지 않을 가능성이 큽니다.



답변

Stream :: reduce 메서드를 사용하여 마지막 요소를 가져올 수 있습니다 . 다음 목록에는 일반적인 경우에 대한 최소한의 예가 포함되어 있습니다.

Stream<T> stream = ...; // sequential or parallel stream
Optional<T> last = stream.reduce((first, second) -> second);

이 구현은 정렬 된 모든 스트림 ( Lists 에서 생성 된 스트림 포함)에 대해 작동합니다 . 주문 되지 않은 경우 스트림이 요소가 반환됩니다 지정되지 않은 분명한 이유입니다.

구현은 순차병렬 스트림 모두에서 작동 합니다 . 언뜻보기에는 놀랍고 불행히도 문서에 명시 적으로 명시되어 있지 않습니다. 그러나 이것은 스트림의 중요한 기능이며 명확히하려고합니다.

  • 방법에 대한 자바 독 스트림 :: 감소 가 있는지, 상태 “입니다 하지 실행하는 데 제약이 순차적으로 .
  • Javadoc은 또한 “누산기 함수가 두 값을 결합하기위한 연관성 , 비 간섭 성 , 상태 비 저장 함수 여야 함”을 요구합니다. 이는 분명히 람다 표현식의 경우입니다.(first, second) -> second .
  • 축소 작업을 위한 Javadoc은 다음과 같이 설명합니다. “스트림 클래스에는 reduce ()collect () [..] 라고하는 여러 형태의 일반 축소 작업이 있습니다 .”“적절하게 구성된 축소 작업은 본질적으로 병렬화 할 수 있습니다. ) 요소를 처리하는 데 사용되는 것은 연관성 이며 상태 비 저장 입니다. “

밀접하게 관련된 Collector에 대한 문서 는 훨씬 더 명확합니다. 순차병렬 실행이 동등한 결과를 생성하도록 하려면 수집기 함수가 ID 및 연관성 제약 조건을 충족해야합니다 .”


원래 질문으로 돌아 가기 : 다음 코드는 변수의 마지막 요소에 대한 참조를 저장 last하고 스트림이 비어있는 경우 예외를 발생시킵니다. 복잡성은 스트림의 길이에 선형 적입니다.

CArea last = data.careas
                 .stream()
                 .filter(c -> c.bbox.orientationHorizontal)
                 .reduce((first, second) -> second).get();


답변

컬렉션 (또는 더 일반적인 Iterable)이있는 경우 Google Guava의

Iterables.getLast(myIterable)

편리한 oneliner로.


답변

라이너 1 개 (흐름 불필요) :

Object lastElement = list.get(list.size()-1);


답변

Guava에는이 경우에 대한 전용 방법이 있습니다.

Stream<T> stream = ...;
Optional<T> lastItem = Streams.findLast(stream);

이와 동등 stream.reduce((a, b) -> b)하지만 제작자는 훨씬 더 나은 성능을 제공한다고 주장합니다.

에서 문서 :

이 메서드의 런타임은 O (log n)와 O (n) 사이에 있으며 효율적으로 분할 가능한 스트림에서 더 잘 수행됩니다.

스트림의 순서가 지정되지 않은 경우이 메서드는 findAny().


답변

마지막 N 개의 요소를 가져와야하는 경우. 클로저를 사용할 수 있습니다. 아래 코드는 스트림이 끝날 때까지 고정 된 크기의 외부 큐를 유지합니다.

    final Queue<Integer> queue = new LinkedList<>();
    final int N=5;
    list.stream().peek((z) -> {
        queue.offer(z);
        if (queue.size() > N)
            queue.poll();
    }).count();

또 다른 옵션은 ID를 대기열로 사용하여 축소 작업을 사용하는 것입니다.

    final int lastN=3;
    Queue<Integer> reduce1 = list.stream()
    .reduce(
        (Queue<Integer>)new LinkedList<Integer>(),
        (m, n) -> {
            m.offer(n);
            if (m.size() > lastN)
               m.poll();
            return m;
    }, (m, n) -> m);

    System.out.println("reduce1 = " + reduce1);


답변

다음과 같이 skip () 함수를 사용할 수도 있습니다.

long count = data.careas.count();
CArea last = data.careas.stream().skip(count - 1).findFirst().get();

사용하기 매우 간단합니다.


답변