[java] for 루프와 for-each 루프간에 성능 차이가 있습니까?

다음 두 루프 사이의 성능 차이는 무엇입니까?

for (Object o: objectArrayList) {
    o.DoSomething();
}

for (int i=0; i<objectArrayList.size(); i++) {
    objectArrayList.get(i).DoSomething();
}



답변

Joshua Bloch의 효과적인 Java 항목 46에서 :

릴리스 1.5에서 소개 된 for-each 루프는 반복자 또는 인덱스 변수를 완전히 숨겨서 혼란을 없애고 오류가 발생할 기회를 제거합니다. 결과 관용구는 컬렉션과 배열에 동일하게 적용됩니다.

// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
    doSomething(e);
}

콜론 (:)이 보이면 “in”으로 읽습니다. 따라서 위의 루프는 “요소의 각 요소 e에 대해”로 읽습니다. 배열에 대해서도 for-each 루프를 사용하면 성능이 저하되지 않습니다. 실제로, 배열 인덱스의 한계를 한 번만 계산하므로 일부 환경에서는 일반 for 루프보다 약간의 성능 이점을 제공 할 수 있습니다. 이 작업을 직접 수행 할 수 있지만 (항목 45) 프로그래머가 항상 그렇게하는 것은 아닙니다.


답변

이 모든 루프는 똑같이 작동합니다. 두 센트를 던지기 전에 이것을 보여주고 싶습니다.

먼저 List를 반복하는 고전적인 방법입니다.

for (int i=0; i < strings.size(); i++) { /* do something using strings.get(i) */ }

둘째, 오류가 적기 때문에 선호되는 방법입니다 (루프 내의 루프에서 변수 i와 j를 혼합 한 횟수는 몇 번입니까?).

for (String s : strings) { /* do something using s */ }

셋째, 마이크로 최적화 된 for 루프 :

int size = strings.size();
for (int i = -1; ++i < size;) { /* do something using strings.get(i) */ }

이제 실제 2 센트 : 적어도 이것을 테스트 할 때 세 번째는 간단한 조작으로 각 유형의 루프에 걸리는 시간에 대해 밀리 초를 계산할 때 가장 빠르며 수백만 번 반복되었습니다. 누군가가 관심이 있다면 Windows에서 jre1.6u10으로.

적어도 세 번째 것이 가장 빠를 것 같지만, 실제로 본 루핑은 루핑 코드의 어디에서나이 구멍 최적화를 구현할 위험을 감수하고 싶은지 스스로에게 물어봐야합니다. t 대부분의 실제 프로그램에서 가장 많은 시간을 소비합니다 (또는 아마도 잘못된 분야에서 일하고 있습니다). 또한 Java for-each 루프 의 서문에서 언급 한 것처럼 (일부는 Iterator 루프라고 하며 다른 것은 for-in 루프라고합니다. ) 사용할 때 특정 바보 같은 버그를 칠 가능성이 적습니다. 그리고 이것이 어떻게 다른 것보다 더 빠를 수 있는지에 대해 토론하기 전에 javac는 바이트 코드를 전혀 최적화하지 않는다는 것을 기억하십시오 (거의 거의 어쨌든). 그냥 컴파일합니다.

마이크로 최적화에 있고 소프트웨어가 재귀 루프를 많이 사용하는 경우 세 번째 루프 유형에 관심이있을 수 있습니다. for 루프를 이처럼 미세하게 최적화 된 루프로 변경하기 전후에 소프트웨어를 벤치마킹해야합니다.


답변

for-each 루프가 일반적으로 선호됩니다. 사용중인 List 구현이 임의 액세스를 지원하지 않으면 “get”접근 방식이 느려질 수 있습니다. 예를 들어, LinkedList가 사용되면 순회 비용이 발생하지만 for-each 방식은 목록에서 해당 위치를 추적하는 반복자를 사용합니다. for-each 루프뉘앙스 에 대한 추가 정보 .

나는 기사가 지금 여기에 있다고 생각한다 : 새로운 위치

여기에 표시된 링크가 죽었습니다.


답변

성능에 미치는 영향은 거의 중요하지 않지만 0은 아닙니다. RandomAccess인터페이스의 JavaDoc을 보면 :

경험상, 클래스의 일반적인 인스턴스에 대해 다음 루프가 발생하면 List 구현에서이 인터페이스를 구현해야합니다.

for (int i=0, n=list.size(); i < n; i++)
    list.get(i);

이 루프보다 빠르게 실행됩니다.

for (Iterator i=list.iterator(); i.hasNext();)
      i.next();

for-each 루프는 반복자와 함께 버전을 사용하므로 ArrayListfor-each 루프가 가장 빠르지 않습니다.


답변

불행히도 차이가있는 것 같습니다.

두 종류의 루프에 대해 생성 된 바이트 코드를 보면 서로 다릅니다.

다음은 Log4j 소스 코드의 예입니다.

/log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java에는 다음을 정의하는 Log4jMarker라는 정적 내부 클래스가 있습니다.

    /*
     * Called from add while synchronized.
     */
    private static boolean contains(final Marker parent, final Marker... localParents) {
        //noinspection ForLoopReplaceableByForEach
        for (final Marker marker : localParents) {
            if (marker == parent) {
                return true;
            }
        }
        return false;
    }

표준 루프 사용시 :

  private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
    Code:
       0: iconst_0
       1: istore_2
       2: aload_1
       3: arraylength
       4: istore_3
       5: iload_2
       6: iload_3
       7: if_icmpge     29
      10: aload_1
      11: iload_2
      12: aaload
      13: astore        4
      15: aload         4
      17: aload_0
      18: if_acmpne     23
      21: iconst_1
      22: ireturn
      23: iinc          2, 1
      26: goto          5
      29: iconst_0
      30: ireturn

for-each로 :

  private static boolean contains(org.apache.logging.log4j.Marker, org.apache.logging.log4j.Marker...);
    Code:
       0: aload_1
       1: astore_2
       2: aload_2
       3: arraylength
       4: istore_3
       5: iconst_0
       6: istore        4
       8: iload         4
      10: iload_3
      11: if_icmpge     34
      14: aload_2
      15: iload         4
      17: aaload
      18: astore        5
      20: aload         5
      22: aload_0
      23: if_acmpne     28
      26: iconst_1
      27: ireturn
      28: iinc          4, 1
      31: goto          8
      34: iconst_0
      35: ireturn

THAT Oracle은 무엇입니까?

Windows 7에서 Java 7 및 8로 이것을 시도했습니다.


답변

인덱싱 대신 반복자를 사용하는 것이 항상 좋습니다. 인덱스 (get 호출)가 그렇지 않은 경우 반복자가 List 구현에 대해 최적화되었을 가능성이 높기 때문입니다. 예를 들어 LinkedList는 List이지만 해당 요소를 통한 색인 작성은 반복자를 사용하여 반복하는 것보다 느립니다.


답변

foreach는 코드의 의도를보다 명확하게 해주 며 일반적으로 아주 작은 속도 향상 (있는 경우)보다 선호됩니다.

인덱싱 된 루프를 볼 때마다 그것이 생각 하는 대로 작동하는지 확인하기 위해 조금 더 구문 분석해야합니다 .

내 시간의 대부분은 코드 (내가 쓴 사람이나 다른 사람이 쓴)를 읽는 데 소비되는 것으로 보이며 명확성은 거의 항상 성능보다 중요합니다. Hotspot은 놀라운 일을하기 때문에 요즘에는 성능을 쉽게 해제 할 수 있습니다.