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


118

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

어디 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모든 요소에를 사용하는 것이 포함되며 성능을 저하시킬 수 있으므로 일반적으로 원하지 않을 가능성이 큽니다.


10
Guava는 Iterables.getLastIterable을 사용하지만 List. 애완 동물 비웃음은 getFirst. Stream일반적으로 API는 편리한 메소드를 많이 생략 끔찍 항문입니다. C #의 LINQ는 constrast에 의해 제공하는 행복 .Last()도하고 .Last(Func<T,Boolean> predicate), 그것은 무한 Enumerables을 지원하더라도 너무.
Aleksandr Dubinsky 2014

@AleksandrDubinsky가 찬성했지만 독자를위한 메모입니다. StreamAPI는 LINQ매우 다른 패러다임에서 수행되기 때문에 완전히 비교할 수 없습니다 . 더 나쁘거나 나아진 것이 아니라 단지 다릅니다. 그리고 오라클 개발자가 무능하거나 비열한 것이 아니기 때문에 확실히 일부 방법이 없습니다. :)
fasth

1
진정한 원 라이너의 경우이 스레드 를 사용할 수 있습니다.
quantum

답변:


185

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();

감사합니다! 그런데 _매개 변수가 필요하지 않은 경우 이름을 생략 할 수 있는지 (아마도 a 또는 이와 유사한 것을 사용하여 ) 알고 있습니까? 따라서 .reduce((_, current) -> current)aws 유효한 구문 만 있으면됩니다.
skiwi

2
@skiwi 모든 유효한 변수 이름을 사용할 수 있습니다 ( 예 : .reduce(($, current) -> current)또는 .reduce((__, current) -> current)(이중 밑줄)).
assylias 2014 년

2
기술적으로 어떤 스트림에서도 작동하지 않을 수 있습니다. 당신이 가리키는 문서 는 만남 순서를 따르는 Stream.reduce(BinaryOperator<T>)경우 언급하지 않으며 reduce, 스트림이 순서가 지정 되더라도 터미널 작업은 만남 순서를 무시할 수 있습니다. 제쳐두고, "commutative"라는 단어는 Stream javadocs에 나타나지 않으므로 그 부재는 우리에게 많은 것을 알려주지 않습니다.
Aleksandr Dubinsky 2014

2
@AleksandrDubinsky : 정확히,이 문서는 축소 작업 과 관련이 없기 때문에 commutative를 언급하지 않습니다 . 중요한 부분은 다음과 같습니다. "[..] 적절하게 구성된 축소 작업은 요소를 처리하는 데 사용되는 함수가 연관 [..] 인 한 본질적으로 병렬화 할 수 있습니다."
nosid

2
@Aleksandr Dubinsky : 물론 "사양에 대한 이론적 질문"이 아닙니다. reduce((a,b)->b)마지막 요소 (물론 순서가 지정된 스트림)를 가져 오는 올바른 솔루션인지 아닌지 간에 차이를 만듭니다 . Brian Goetz의 진술은 더 나아가 API 문서reduce("", String::concat) 에서 문자열 연결에 대한 비효율적이지만 올바른 솔루션 이라고 명시하고 있으며, 이는 만남 순서의 유지를 의미합니다.
Holger

42

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

Iterables.getLast(myIterable)

편리한 oneliner로.


1
스트림을 iterable로 쉽게 변환 할 수 있습니다.Iterables.getLast(() -> data.careas.stream().filter(c -> c.bbox.orientationHorizontal).iterator())
shmosel

10

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

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

30
목록이 비어 있으면이 코드는 ArrayIndexOutOfBoundsException.
Dragon

8

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

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

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

에서 문서 :

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

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


1
종류의 ... 홀가의 @ZhekaKozlov는 몇 가지 결함을 보여 여기에
유진

0

마지막 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);

-1

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

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

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


참고 : "건너 뛰기"는 N 번째 숫자에 도달 할 때까지 모든 요소를 ​​반복하여 구현되므로 방대한 컬렉션 (수백만 개의 항목)을 처리 할 때 스트림의 "건너 뛰기"에 의존해서는 안됩니다. 시도했습니다. 간단한 인덱스 별 작업에 비해 성능에 매우 실망했습니다.
java.is.for.desktop

1
목록이 비어있는 경우에도, 그것은 발생합니다ArrayIndexOutOfBoundsException
Jindra Vysocký에게
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.