java8 스트림에서 처리 순서를 보장하는 방법은 무엇입니까?


148

XMLJava 객체 내부에서 목록을 처리하고 싶습니다 . 모든 요소를 ​​순서대로 처리해야합니다.

그러므로 내가 사용 sequential하는 각각을 불러야 stream합니까? list.stream().sequential().filter().forEach()

아니면 병렬 처리를 사용하지 않는 한 스트림을 사용하는 것으로 충분합니까? list.stream().filter().forEach()

답변:


338

당신은 잘못된 질문을하고 있습니다. 당신은 sequential대를 요구하고있는 parallel반면 당신 은 순서대로 아이템을 처리 하기 를 원하기 때문에, 당신은 주문 에 관해 물어야 합니다. 순서지정된 스트림 이 있고 순서 를 유지하도록 보장하는 작업을 수행하는 경우 스트림이 병렬로 처리되는지 순차적으로 처리되는지는 중요하지 않습니다. 구현은 순서를 유지합니다.

정렬 된 속성은 병렬 및 순차적 속성과 다릅니다. 당신이 호출하는 경우 예는 stream()A의 HashSet호출하는 동안 스트림이 정렬되지 않은 것 stream()A의 List수익률 정렬 된 스트림입니다. 전화 unordered()를 걸어 주문 계약을 해제하고 잠재적으로 성능을 향상시킬 수 있습니다. 스트림에 순서가 없으면 순서를 다시 설정할 수있는 방법이 없습니다. 정렬되지 않은 스트림을 정렬 된 순서로 변환하는 유일한 방법은을 호출하는 것입니다 sorted. 그러나 결과 순서가 반드시 원래 순서는 아닙니다.

또한 참조 "주문"섹션java.util.stream패키지 문서를 .

전체 스트림 작업에서 순서를 유지하려면 스트림 소스, 모든 중간 작업 및 터미널 작업의 순서를 유지하는지 여부 (또는 소스의 순서가 첫 번째인지 여부)에 대한 설명서를 연구해야합니다. 장소).

예를 들어 정렬되지 않은 스트림 Stream.iterate(T,UnaryOperator)Stream.generate(Supplier)만드는 동안 정렬 된 스트림을 만드는 것은 매우 미묘 할 수 있습니다 . 주문을 유지 하지 않기 때문에 귀하의 질문에서 일반적인 실수를 범했습니다 . 스트림 요소를 보장 된 순서로 처리하려면 사용해야 합니다.forEach forEachOrdered

따라서 list귀하의 질문에 실제로 a 인 java.util.List경우 해당 stream()메소드는 정렬 된 스트림 을 반환하고 순서를filter 변경하지 않습니다. 따라서을 호출 list.stream().filter() .forEachOrdered()하면 모든 요소가 순차적으로 처리되는 반면 list.parallelStream().filter().forEachOrdered()요소가 병렬로 처리 될 수 있지만 (예 : 필터에 의해) 터미널 동작이 순서대로 호출됩니다 (병렬 실행의 이점이 감소 함) .

예를 들어 다음과 같은 작업을 사용하는 경우

List<…> result=inputList.parallelStream().map(…).filter(…).collect(Collectors.toList());

전체 작업은 병렬 실행의 이점이 있지만 병렬 또는 순차 스트림 사용 여부에 관계없이 결과 목록은 항상 올바른 순서로 표시됩니다.


48
예, 좋은 대답입니다. 내가 찾은 것 중 하나는 적어도 "이전", "이후"등과 같이 영어로 사용하는 용어가 매우 모호하다는 것입니다. 여기에는 두 가지 종류의 순서가 있습니다 : 1) 만남 순서 ( 공간 순서 라고도 함 ) 및 2) 처리 순서 ( 시간 순서 라고도 함 ). 이러한 차이점을 염두에두고 만남 순서를 논의 할 때는 "왼쪽"또는 "오른쪽"과 같은 단어를 사용하고 처리 순서를 논의 할 때는 "이전"또는 "이후"와 같은 단어를 사용하는 것이 도움이 될 수 있습니다.
스튜어트 마크

나는 List<>질서를 지키겠다고 이해 하지만 Collection<>?
Josh C.

5
@JoshC. 실제 컬렉션 유형에 따라 다릅니다. SetSortedSet또는이 아닌 한 일반적으로 그렇지 않습니다 LinkedHashSet. Map( keySet(), entrySet()values()) 의 모음보기는 Map의 정책을 상속합니다 . 즉,지도가 SortedMap또는 인 경우 순서가 정해 LinkedHashMap집니다. 동작은 컬렉션의 spliterator에 의해보고 된 특성에 의해 결정됩니다 . 의 default구현은 특성을 Collection보고하지 않으므로 ORDERED재정의되지 않는 한 순서가 맞지 않습니다.
Holger

@Holger 나는 당신의 대답의 작은 부분과 다소 관련 이있는 질문을 했습니다.
나만

1
병렬 스트림 forEachOrderedforEach사용할 때 와는 다른 점에 주목할 필요가 있습니다. 그러나 김이 나는 방법이 변경 될 경우를 대비하여 주문할 때이를 사용하는 것이 좋습니다 ...
Steve Chambers

0

간단히 말해서 :

순서는 소스 데이터 구조 및 중간 스트림 조작에 따라 다릅니다. List처리를 사용한다고 가정하면 filter여기에서 순서가 변경되지 않으므로 처리를 주문해야합니다 .

자세한 내용은:

순차 vs 병렬 vs 비 순차 :

자바 독

S sequential()
Returns an equivalent stream that is sequential. May return itself, either because the stream was already sequential, or because the underlying stream state was modified to be sequential.
This is an intermediate operation.
S parallel()
Returns an equivalent stream that is parallel. May return itself, either because the stream was already parallel, or because the underlying stream state was modified to be parallel.
This is an intermediate operation.
S unordered()
Returns an equivalent stream that is unordered. May return itself, either because the stream was already unordered, or because the underlying stream state was modified to be unordered.
This is an intermediate operation.

스트림 주문 :

자바 독

스트림에는 정의 된 만남 순서가 있거나 없을 수 있습니다. 스트림에 발생 순서가 있는지 여부는 소스 및 중간 작업에 따라 다릅니다. 특정 스트림 소스 (예 : List 또는 배열)는 본질적으로 정렬되어 있지만 다른 스트림 소스 (예 : HashSet)는 그렇지 않습니다. sorted ()와 같은 일부 중간 작업은 정렬되지 않은 스트림에서 발생 순서를 부과 할 수 있으며, 다른 일부는 BaseStream.unordered ()와 같이 정렬 된 스트림을 정렬되지 않은 상태로 렌더링 할 수 있습니다. 또한 일부 터미널 작업은 forEach ()와 같은 만남 순서를 무시할 수 있습니다.

스트림이 정렬되면 대부분의 작업은 요소에 대해 발생 순서대로 작동하도록 제한됩니다. 스트림의 소스가 [1, 2, 3]을 포함하는 List 인 경우 map (x-> x * 2) 실행 결과는 [2, 4, 6]이어야합니다. 그러나 소스에 정의 된 조우 순서가 없으면 값 [2, 4, 6]의 순열이 올바른 결과가됩니다.

순차적 스트림의 경우 발생 순서의 유무는 성능에 영향을 미치지 않으며 결정성에 만 영향을줍니다. 스트림이 주문 된 경우 동일한 소스에서 동일한 스트림 파이프 라인을 반복 실행하면 동일한 결과가 생성됩니다. 순서가 지정되지 않은 경우 반복 된 실행으로 인해 다른 결과가 발생할 수 있습니다.

병렬 스트림의 경우 순서 제약 조건을 완화하면 더 효율적인 실행이 가능할 수 있습니다. 중복 요소 필터링 (distinct ()) 또는 그룹화 된 축소 (Collectors.groupingBy ())와 같은 특정 집계 작업은 요소 순서가 적절하지 않은 경우보다 효율적으로 구현 될 수 있습니다. 마찬가지로 limit ()과 같이 순서를 만나기 위해 본질적으로 연결된 작업은 병렬 처리의 이점을 손상시키는 적절한 순서를 보장하기 위해 버퍼링이 필요할 수 있습니다. 스트림에 발생 순서가 있지만 사용자가 해당 발생 순서를 특별히 신경 쓰지 않는 경우 unorder ()로 스트림을 명시 적으로 순서를 바꾸면 일부 상태 저장 또는 터미널 작업의 병렬 성능이 향상 될 수 있습니다. 그러나 위의 "블록 무게의 합"과 같은 대부분의 스트림 파이프 라인은

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.