답변:
설명 된 것과 같은 간단한 경우에는 대부분 동일합니다. 그러나 중요한 몇 가지 미묘한 차이점이 있습니다.
한 가지 문제는 주문과 관련이 있습니다. 를 사용 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
일관성이없는 동작이 발생할 수 있습니다.
ArrayList
은 동시 수정을 상당히 엄격하게 검사하므로 종종 throw ConcurrentModificationException
됩니다. 그러나 이것은 특히 병렬 스트림의 경우 보장되지 않습니다. CME 대신 예기치 않은 답변을 얻을 수 있습니다. 스트림 소스에 대한 비 구조적 수정도 고려하십시오. 병렬 스트림의 경우 특정 요소를 처리 할 스레드가 무엇인지, 수정 당시 처리되었는지 여부를 알 수 없습니다. 이것은 각 경기에서 다른 결과를 얻을 수 있고 CME를 얻지 못하는 경쟁 조건을 설정합니다.
이 답변은 다양한 루프 구현의 성능과 관련이 있습니다. 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
내용을 한 번 반복하고 반복 하는 동안 요소를 인쇄합니다.
Stream
는 생성 중이거나 개별 객체 를 참조하고 있습니까? AFAIK, a Stream
는 요소를 복제하지 않습니다.
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
Iterable.forEach takes the collection's lock
. 이 정보는 어디에서 왔습니까? JDK 소스에서 이러한 동작을 찾을 수 없습니다.