를 사용하면 병렬 .stream()
작업과 같은 체인 작업을 .filter()
사용하거나 병렬 스트림을 사용할 수 있음을 이해 합니다. 그러나 작은 작업을 수행 해야하는 경우 (예 : 목록 요소 인쇄) 차이점은 무엇입니까?
collection.stream().forEach(System.out::println);
collection.forEach(System.out::println);
답변
설명 된 것과 같은 간단한 경우에는 대부분 동일합니다. 그러나 중요한 몇 가지 미묘한 차이점이 있습니다.
한 가지 문제는 주문과 관련이 있습니다. 를 사용 Stream.forEach
하면 순서가 정의되지 않습니다 . 순차적 인 스트림에서는 발생하지 않지만 여전히 Stream.forEach
임의의 순서로 실행 하기위한 사양 내에 있습니다. 이것은 병렬 스트림에서 자주 발생합니다. 대조적으로, 는 지정된 경우 Iterable.forEach
항상 반복 순서대로 실행 Iterable
됩니다.
또 다른 문제는 부작용입니다. 에 지정된 조치 Stream.forEach
는 방해 가 되지 않아야 합니다. ( java.util.stream 패키지 doc 참조 ) Iterable.forEach
잠재적으로 제한이 적습니다. 에서 컬렉션의 경우 java.util
, Iterable.forEach
일반적으로 그 수집의 사용 Iterator
설계되어 대부분의 것은 할 수 르파 하고있는이 발생합니다 ConcurrentModificationException
컬렉션이 구조적으로 반복 중에 수정되는 경우. 그러나, 구조없는 변형 되는 반복 동안시켰다. 예를 들어, ArrayList 클래스 문서 는 “단지 요소의 값을 설정하는 것은 구조적인 수정이 아닙니다”라고 말합니다. 따라서ArrayList.forEach
ArrayList
문제없이 기본 값을 설정할 수 있습니다.
동시 컬렉션은 아직 다릅니다. 빠른 실패 대신 약하게 일관성있게 설계되었습니다 . 전체 정의는 해당 링크에 있습니다. 그러나 간단히 생각해보십시오 ConcurrentLinkedDeque
. 그 전달 동작 forEach
방법은 있다 하더라도 구조적으로, 기본 양단 큐를 수정할 수 및 ConcurrentModificationException
발생하지 않습니다. 그러나이 수정에서 발생하는 수정 내용이 표시되거나 표시되지 않을 수 있습니다. (따라서 “약한”일관성.)
Iterable.forEach
동기화 된 컬렉션을 반복하는 경우 또 다른 차이점이 있습니다 . 이러한 콜렉션에서는 Iterable.forEach
콜렉션의 잠금을 한 번 수행하고 조치 메소드에 대한 모든 호출에서 보유합니다. 이 Stream.forEach
호출은 컬렉션의 스플리터를 사용하며,이 스플리터는 잠기지 않으며 일반적인 비 간섭 규칙에 의존합니다. 스트림을 지원하는 콜렉션은 반복 중에 수정 될 수 있으며, 스트림 인 경우 ConcurrentModificationException
일관성이없는 동작이 발생할 수 있습니다.
답변
이 답변은 다양한 루프 구현의 성능과 관련이 있습니다. VERY OFTEN (수백만 건의 호출)이라고하는 루프와는 거의 관련이 없습니다. 대부분의 경우 루프의 내용은 가장 비싼 요소입니다. 루프를 자주 반복하는 상황에서는 여전히 관심이있을 수 있습니다.
구현에 따라 다르므로 ( 전체 소스 코드 ) 대상 시스템에서이 테스트를 반복해야합니다 .
빠른 Linux 시스템에서 openjdk 버전 1.8.0_111을 실행합니다.
integers
(10 ^ 0-> 10 ^ 5 항목)에 대해 다양한 크기 의이 코드를 사용하여 List에서 10 ^ 6 회 반복되는 테스트를 작성했습니다 .
결과는 다음과 같습니다. 가장 빠른 방법은 목록의 항목 수에 따라 다릅니다.
그러나 여전히 최악의 상황에서는 10 ^ 6 회 이상 10 ^ 6 회 반복하는 것이 최악의 수행자에게는 100 초가 걸렸으므로 거의 모든 상황에서 다른 고려 사항이 더 중요합니다.
public int outside = 0;
private void forCounter(List<Integer> integers) {
for(int ii = 0; ii < integers.size(); ii++) {
Integer next = integers.get(ii);
outside = next*next;
}
}
private void forEach(List<Integer> integers) {
for(Integer next : integers) {
outside = next * next;
}
}
private void iteratorForEach(List<Integer> integers) {
integers.forEach((ii) -> {
outside = ii*ii;
});
}
private void iteratorStream(List<Integer> integers) {
integers.stream().forEach((ii) -> {
outside = ii*ii;
});
}
여기 내 타이밍이 있습니다 : 밀리 초 / 기능 / 목록의 항목 수. 각 실행은 10 ^ 6 루프입니다.
1 10 100 1000 10000
iterator.forEach 27 116 959 8832 88958
for:each 53 171 1262 11164 111005
for with index 39 112 920 8577 89212
iterable.stream.forEach 255 324 1030 8519 88419
실험을 반복하면 전체 소스 코드를 게시했습니다 . 이 답변을 편집하고 테스트 한 시스템 표기법으로 결과를 추가하십시오.
MacBook Pro, 2.5GHz Intel Core i7, 16GB, macOS 10.12.6 사용 :
1 10 100 1000 10000
iterator.forEach 27 106 1047 8516 88044
for:each 46 143 1182 10548 101925
for with index 49 145 887 7614 81130
iterable.stream.forEach 393 397 1108 8908 88361
Java 8 핫스팟 VM-3.4GHz Intel Xeon, 8GB, Windows 10 Pro
1 10 100 1000 10000
iterator.forEach 30 115 928 8384 85911
for:each 40 125 1166 10804 108006
for with index 30 120 956 8247 81116
iterable.stream.forEach 260 237 1020 8401 84883
Java 11 핫스팟 VM-3.4GHz Intel Xeon, 8GB, Windows 10 Pro
(위와 동일한 시스템, 다른 JDK 버전)
1 10 100 1000 10000
iterator.forEach 20 104 940 8350 88918
for:each 50 140 991 8497 89873
for with index 37 140 945 8646 90402
iterable.stream.forEach 200 270 1054 8558 87449
Java 11 OpenJ9 VM-3.4GHz Intel Xeon, 8GB, Windows 10 Pro
(위와 동일한 머신 및 JDK 버전, 다른 VM)
1 10 100 1000 10000
iterator.forEach 211 475 3499 33631 336108
for:each 200 375 2793 27249 272590
for with index 384 467 2718 26036 261408
iterable.stream.forEach 515 714 3096 26320 262786
Java 8 핫스팟 VM-2.8GHz AMD, 64GB, Windows Server 2016
1 10 100 1000 10000
iterator.forEach 95 192 2076 19269 198519
for:each 157 224 2492 25466 248494
for with index 140 368 2084 22294 207092
iterable.stream.forEach 946 687 2206 21697 238457
Java 11 핫스팟 VM-2.8GHz AMD, 64GB, Windows Server 2016
(위와 동일한 시스템, 다른 JDK 버전)
1 10 100 1000 10000
iterator.forEach 72 269 1972 23157 229445
for:each 192 376 2114 24389 233544
for with index 165 424 2123 20853 220356
iterable.stream.forEach 921 660 2194 23840 204817
Java 11 OpenJ9 VM-2.8GHz AMD, 64GB, Windows Server 2016
(위와 동일한 머신 및 JDK 버전, 다른 VM)
1 10 100 1000 10000
iterator.forEach 592 914 7232 59062 529497
for:each 477 1576 14706 129724 1190001
for with index 893 838 7265 74045 842927
iterable.stream.forEach 1359 1782 11869 104427 958584
선택한 VM 구현은 또한 Hotspot / OpenJ9 / etc와 차별화됩니다.
답변
당신이 언급 한 둘 사이에는 차이가 없습니다. 적어도 개념적으로 Collection.forEach()
는 약칭입니다.
내부적으로 stream()
버전은 객체 생성으로 인해 약간 더 많은 오버 헤드가 있지만 런타임을 보면 오버 헤드가 없습니다.
두 구현 모두 collection
내용을 한 번 반복하고 반복 하는 동안 요소를 인쇄합니다.
답변
Collection.forEach ()는 컬렉션의 반복자를 사용합니다 (지정된 경우). 이는 품목의 처리 순서가 정의되었음을 의미합니다. 반대로 Collection.stream (). forEach ()의 처리 순서는 정의되어 있지 않습니다.
대부분의 경우, 우리가 선택한 두 가지를 구별하지 않습니다. 병렬 스트림을 사용하면 여러 스레드에서 스트림을 실행할 수 있으며 이러한 상황에서는 실행 순서가 정의되지 않습니다. Java는 Collectors.toList ()와 같은 터미널 조작이 호출되기 전에 모든 스레드 만 완료하면됩니다. 컬렉션에서 직접 forEach ()를 직접 호출하고 병렬 스트림에서 두 번째로 호출하는 예를 살펴 보겠습니다.
list.forEach(System.out::print);
System.out.print(" ");
list.parallelStream().forEach(System.out::print);
코드를 여러 번 실행하면 list.forEach ()가 항목을 삽입 순서대로 처리하는 반면 list.parallelStream (). forEach ()는 각 실행에서 다른 결과를 생성합니다. 가능한 출력은 다음과 같습니다.
ABCD CDBA
다른 하나는 다음과 같습니다.
ABCD DBCA