Java에서 기존 for 루프 대 Iterator / foreach의 성능
ArrayList, HashMap 및 기타 컬렉션을 탐색하는 동안 기존 for 루프와 Iterator를 비교할 때 사용할 수있는 성능 테스트 결과가 있습니까?
아니면 단순히 for 루프를 통해 반복자를 사용해야하는 이유는 무엇입니까?
이것이 당신이 의미하는 바라고 가정합니다.
// traditional for loop
for (int i = 0; i < collection.size(); i++) {
T obj = collection.get(i);
// snip
}
// using iterator
Iterator<T> iter = collection.iterator();
while (iter.hasNext()) {
T obj = iter.next();
// snip
}
// using iterator internally (confirm it yourself using javap -c)
for (T obj : collection) {
// snip
}
반복자는 임의 액세스가없는 컬렉션 (예 : TreeSet, HashMap, LinkedList)에 대해 더 빠릅니다. 배열 및 ArrayList의 경우 성능 차이는 무시해도 좋습니다.
편집 : 마이크로 벤치마킹은 초기 최적화와 마찬가지로 거의 악의 근원이라고 생각합니다. 하지만 다시 말하지만, 그렇게 사소한 일의 의미에 대해 느끼는 것이 좋다고 생각합니다. 따라서 나는 작은 테스트를 실행 했습니다 .
- LinkedList 및 ArrayList를 각각 반복합니다.
- 100,000 개의 "무작위"문자열
- 길이 합산 (컴파일러가 전체 루프를 최적화하는 것을 피하기 위해)
- 3 개의 루프 스타일 모두 사용 (반복자, 각각에 대해, for with counter)
LinkedList를 사용하는 "for with counter"를 제외한 모든 결과는 유사합니다. 나머지 5 개는 전체 목록을 반복하는 데 20 밀리 초도 채 걸리지 않았습니다. list.get(i)
LinkedList에서 100,000 번 사용하면 완료하는 데 2 분 (!) 이상이 걸렸습니다 (60,000 배 느림). 와! :) 따라서 반복기를 사용하는 것이 가장 좋습니다 (각각에 대해 명시 적으로 또는 암시 적으로 사용). 특히 처리하는 목록의 유형과 크기를 모르는 경우에는 더욱 그렇습니다.
반복자를 사용하는 첫 번째 이유는 명백한 정확성 입니다. 수동 색인을 사용하는 경우 매우 자세히 살펴볼 때만 볼 수있는 매우 무해한 일회성 오류가있을 수 있습니다. 1에서 시작 했습니까, 0에서 시작 했습니까? 에서 끝냈습니까 length - 1
? <
또는 사용 했습니까 <=
? 반복자를 사용하면 실제로 전체 배열을 반복하고 있음을 훨씬 쉽게 알 수 있습니다. "당신이하는 일을 말하고 당신이하는 일을하십시오."
두 번째 이유는 서로 다른 데이터 구조에 대한 균일 한 액세스입니다. 인덱스를 통해 배열에 효율적으로 액세스 할 수 있지만 마지막으로 액세스 한 요소를 기억하여 연결 목록을 탐색하는 것이 가장 좋습니다 (그렇지 않으면 " Shlemiel the painter " 를 얻습니다 ). 해시 맵은 훨씬 더 복잡합니다. 이러한 데이터 구조와 다른 데이터 구조에서 균일 한 인터페이스를 제공함으로써 (예 : 트리 탐색도 수행 할 수 있음) 명백한 정확성을 다시 얻을 수 있습니다. 순회 논리는 한 번만 구현하면되며이를 사용하는 코드는 간결하게 "그것이하는 일을 말하고 말한대로 수행"할 수 있습니다.
대부분의 경우 성능이 비슷합니다.
그러나 코드가 List를 수신하고 루프를 반복 할 때마다 잘 알려진 경우가 있습니다.
Iterator는 RandomAccess를 구현하지 않는 모든 List 구현 (예 : LinkedList)에 더 좋습니다.
그 이유는 이러한 목록의 경우 인덱스로 요소에 액세스하는 것이 일정한 시간 작업이 아니기 때문입니다.
따라서 Iterator는 구현 세부 사항에 대해 더 강력한 것으로 간주 할 수도 있습니다.
항상 그렇듯이 성능은 가독성 문제를 숨기지 않아야합니다.
java5 foreach 루프는 그 측면에서 큰 인기를 얻었습니다. :-)
나는 그것을 믿지 않는다
for (T obj : collection) {
루프를 통해 매번 .size ()를 계산하므로
for (int i = 0; i < collection.size(); i++) {
One of the best reasons to use an iterator over the i++ syntax is that not all data structures will support random access let alone have it perform well. You should also be programming to the list or collection interface so that if you later decided that another data structure would be more efficient you'd be able to swap it out without massive surgery. In that case (the case of coding to an interface) you won't necessarily know the implementation details and it's probably wiser to defer that to the data structure itself.
One of the reasons I've learned to stick with the for each is that it simplifies nested loops, especially over 2+ dimensional loops. All the i's, j's, and k's that you may end up manipulating can get confusing very quickly.
Use JAD or JD-GUI against your generated code, and you will see that there is no real difference. The advantage of the new iterator form is that it looks cleaner in your codebase.
Edit: I see from the other answers that you actually meant the difference between using get(i) versus an iterator. I took the original question to mean the difference between the old and new ways of using the iterator.
Using get(i) and maintaining your own counter, especially for the List
classes is not a good idea, for the reasons mentioned in the accepted answer.
Yes, it does make a difference on collections which are not random access based like LinkedList. A linked list internally is implemented by nodes pointing to the next(starting at a head node).
The get(i) method in a linked list starts from the head node and navigates through the links all the way to the i'th node. When you iterate on the linked list using a traditional for loop, you start again from the head node each time, thus the overall traversal becomes quadratic time.
for( int i = 0; i< list.size(); i++ ) {
list.get(i); //this starts everytime from the head node instead of previous node
}
While the for each loop iterates over the iterator obtained from the linked list and calls its next() method. The iterator maintains the states of the last access and thus does not start all the way from head everytime.
for( Object item: list ) {
//item element is obtained from the iterator's next method.
}
+1 to what sfussenegger said. FYI, whether you use an explicit iterator or an implicit one (i.e. for each) won't make a performance difference because they compile to the same byte code.
'Programing' 카테고리의 다른 글
Rails : 하나의 양식에서 다중 제출 버튼 (0) | 2020.12.09 |
---|---|
(function (window, document, undefined) {…}) (window, document)를 사용하면 어떤 이점이 있습니까? (0) | 2020.12.09 |
MS C ++ 2010 및 mspdb100.dll (0) | 2020.12.08 |
메인 스레드는 UI 스레드와 동일합니까? (0) | 2020.12.08 |
웹 사이트 스테이징을위한 Git Post-Receive Hook (0) | 2020.12.08 |