JavaRush /Java Blog /Random-KO /커피 브레이크 #137. For 루프 또는 Foreach - Java에서 어느 것이 더 빠릅니까? Ja...

커피 브레이크 #137. For 루프 또는 Foreach - Java에서 어느 것이 더 빠릅니까? Java 맵의 각 항목을 반복하는 8가지 효율적인 방법

Random-KO 그룹에 게시되었습니다

For 루프 또는 Foreach - Java에서 어느 것이 더 빠릅니까?

출처: 중간 몇 년 전 일자리를 찾을 때 인터뷰에서 받았던 질문 중 하나는 for 또는 forEach를 사용하여 ArrayList를 반복해야 하는지 여부였습니다 . forEachfor 간의 선호도 차이에 대한 논쟁은 오랫동안 진행되어 왔습니다. 나는 forEach 가 더 빠르다 는 인상을 받았습니다 . 그러나 결국 나는 내가 틀렸다는 것을 깨달았습니다. 참고로 Java 1.5에 도입된 forEach 루프 (또는 개선된 for 루프 )는 반복자 또는 인덱스 변수를 완전히 숨김으로써 혼란과 오류 가능성을 제거합니다. forforEach 의 유일한 실질적인 차이점은 인덱스된 객체의 경우 인덱스에 액세스할 수 없다는 것입니다. 커피 브레이크 #137.  For 루프 또는 Foreach - Java에서 어느 것이 더 빠릅니까?  Java 맵의 각 항목을 반복하는 8가지 효율적인 방법 - 1
for(int i = 0; i < mylist.length; i++) {
 if(i < 5) {
 //do something
 } else {
 //do other stuff
 }
}
그러나 forEach를 사용하면 int 유형의 별도 인덱스 변수를 만들 수 있습니다 . 예를 들어:
int index = -1;
for(int myint : mylist) {
 index++;
 if(index < 5) {
 //do something
 } else {
 //do other stuff
 }
}
forEach를 사용하여 목록을 반복하는 foreachTest() 메서드가 있는 간단한 클래스를 작성해 보겠습니다 .
import java.util.List;

public class ForEachTest {
	List<Integer> intList;

    public void foreachTest(){
        for(Integer i : intList){

        }
    }
}
이 클래스를 컴파일할 때 컴파일러는 내부적으로 코드를 반복기 구현으로 변환합니다. javap -verbose IterateListTest 를 실행하여 컴파일된 코드를 디컴파일했습니다 .
public void foreachTest();
   descriptor: ()V
   flags: ACC_PUBLIC
   Code:
     stack=1, locals=3, args_size=1
        0: aload_0
        1: getfield      #19                 // Field intList:Ljava/util/List;
        4: invokeinterface #21,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;
        9: astore_2
       10: goto          23
       13: aload_2
       14: invokeinterface #27,  1           // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
       19: checkcast     #33                 // class java/lang/Integer
       22: astore_1
       23: aload_2
       24: invokeinterface #35,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z
       29: ifne          13
       32: return
     LineNumberTable:
       line 9: 0
       line 12: 32
     LocalVariableTable:
       Start  Length  Slot  Name   Signature
           0      33     0  this   Lcom/greekykhs/springboot/ForEachTest;
     StackMapTable: number_of_entries = 2
       frame_type = 255 /* full_frame */
         offset_delta = 13
         locals = [ class com/greekykhs/springboot/ForEachTest, top, class java/util/Iterator ]
         stack = []
       frame_type = 9 /* same */
위의 바이트코드에서 다음을 볼 수 있습니다:
  1. getfield 명령은 정수 변수를 얻는 데 사용됩니다.

  2. List.iterator를 호출하여 반복자 인스턴스를 가져옵니다.

  3. iterator.hasNext 를 호출하세요 . true를 반환하면 iterator.next 메서드를 호출해야 합니다 .

성능 테스트를 실행해 보겠습니다. 기본 IterateListTest 메서드 에서 목록을 만들고 forforEach 루프를 사용하여 목록을 반복했습니다 .
import java.util.ArrayList;
import java.util.List;

public class IterateListTest {
	public static void main(String[] args) {
		List<Integer> mylist = new ArrayList<>();
        for (int i = 0; i < 1000000; i++) {
            mylist.add(i);
        }

        long forLoopStartTime = System.currentTimeMillis();
        for (int i = 0; i < mylist.size(); i++) {mylist.get(i);}

        long forLoopTraversalCost =System.currentTimeMillis()-forLoopStartTime;
        System.out.println("for loop traversal cost for ArrayList= "+ forLoopTraversalCost);

        long forEachStartTime = System.currentTimeMillis();
        for (Integer integer : mylist) {}

        long forEachTraversalCost =System.currentTimeMillis()-forEachStartTime;
        System.out.println("foreach traversal cost for ArrayList= "+ forEachTraversalCost);
	}
}
결과는 다음과 같습니다. 커피 브레이크 #137.  For 루프 또는 Foreach - Java에서 어느 것이 더 빠릅니까?  Java 맵의 각 항목을 반복하는 8가지 효율적인 방법 - 2보시다시피 for 루프의 성능은 forEach 루프 보다 좋습니다 . ArrayList 대신 LinkedList를 사용하면 LinkedList 의 forEach 성능이 더 우수하다는 것을 알 수 있습니다 . ArrayList는 내부적으로 배열을 사용하여 요소를 저장합니다. 배열은 메모리의 연속 영역이므로 시간 복잡도는 O(1)입니다. 이는 인덱스를 통해 데이터를 검색하기 때문입니다. LinkedList는 이중 연결리스트를 사용합니다. 순회를 구현하기 위해 for 루프를 사용할 때 마다 연결 리스트의 헤드 노드부터 시작하므로 시간 복잡도는 O(n*n)입니다.

Java 맵의 각 항목을 반복하는 8가지 효율적인 방법

출처: Medium 지난주에 한 인턴이 나에게 Java Map을 반복하는 방법을 물었습니다. 저는 아주 간단하기 때문에 이 질문에 대한 답은 항상 구글에 있다고 답했습니다. 얼마 후 그녀는 나에게 StackOverflow 페이지 주소를 보냈고 많은 사람들이 이 문제에 관심을 갖고 있다는 것을 알게 되었습니다. 따라서 나는 반복 문제에 대해 자세히 설명하고 이를 수행하는 몇 가지 방법을 여러분과 공유하기로 결정했습니다. 커피 브레이크 #137.  For 루프 또는 Foreach - Java에서 어느 것이 더 빠릅니까?  Java 맵의 각 항목을 반복하는 8가지 효율적인 방법 - 3

1. 반복자와 Map.Entry 사용

@Test
public void test1_UsingWhileAndMapEntry(){
    long i = 0;
    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
    while (it.hasNext()) {
        Map.Entry<Integer, Integer> pair = it.next();
        i += pair.getKey() + pair.getValue();
    }
    System.out.println(i);
}

2. foreach 및 Map.Entry 사용

@Test
public void test2_UsingForEachAndMapEntry(){
    long i = 0;
    for (Map.Entry<Integer, Integer> pair : map.entrySet()) {
        i += pair.getKey() + pair.getValue();
    }
    System.out.println(i);
}

3. Java 8에서 foreach 사용

@Test
public void test3_UsingForEachAndJava8(){
    final long[] i = {0};
    map.forEach((k, v) -> i[0] += k + v);
    System.out.println(i[0]);
}

4. keySet 및 foreach 사용

@Test
public void test4_UsingKeySetAndForEach(){
    long i = 0;
    for (Integer key : map.keySet()) {
        i += key + map.get(key);
    }
    System.out.println(i);
}

5. keySet 및 반복자 사용

@Test
public void test5_UsingKeySetAndIterator(){
    long i = 0;
    Iterator<Integer> it = map.keySet().iterator();
    while (it.hasNext()) {
        Integer key = it.next();
        i += key + map.get(key);
    }
    System.out.println(i);
}

6. for 및 Map.Entry 사용

@Test
public void test6_UsingForAndIterator(){
    long i = 0;
    for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {
        Map.Entry<Integer, Integer> entry = entries.next();
        i += entry.getKey() + entry.getValue();
    }
    System.out.println(i);
}

7. Java 8 스트림 API 사용

@Test
public void test7_UsingJava8StreamApi(){
    System. out .println(map.entrySet().stream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}

8. Java 8 Stream API의 병렬 사용

@Test
public void test8_UsingJava8StreamApiParallel(){
    System. out .println(map.entrySet().parallelStream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}

속도 측면에서 각 방법의 비교:

public final static Integer SIZE = 1000000;
public Map<Integer, Integer> map = toMap();
public Map<Integer, Integer> toMap(){
    map = new HashMap<>(SIZE);
    for (int i = 0; i < SIZE; i++) {
        map.put(i, i);
    }
    return map;
}
우리는 다음을 얻습니다: 커피 브레이크 #137.  For 루프 또는 Foreach - Java에서 어느 것이 더 빠릅니까?  Java 맵의 각 항목을 반복하는 8가지 효율적인 방법 - 4커피 브레이크 #137.  For 루프 또는 Foreach - Java에서 어느 것이 더 빠릅니까?  Java 맵의 각 항목을 반복하는 8가지 효율적인 방법 - 5커피 브레이크 #137.  For 루프 또는 Foreach - Java에서 어느 것이 더 빠릅니까?  Java 맵의 각 항목을 반복하는 8가지 효율적인 방법 - 6

결론

데이터를 비교해보면 방법 6이 가장 많은 시간이 걸리고, 방법 8이 숫자가 작을 때는 가장 많은 시간이 걸리고, 숫자가 클 때는 방법 8이 동시에 실행되기 때문에 시간이 가장 적게 걸리는 것을 알 수 있다. 흥미로운 점은 테스트 실행 순서가 항상 -> while -> foreach/stream이고 왜 그런지 모르겠습니다 :(
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION