For 루프 또는 Foreach - Java에서 어느 것이 더 빠릅니까?
출처: 중간 몇 년 전 일자리를 찾을 때 인터뷰에서 받았던 질문 중 하나는 for 또는 forEach를 사용하여 ArrayList를 반복해야 하는지 여부였습니다 . forEach 와 for 간의 선호도 차이에 대한 논쟁은 오랫동안 진행되어 왔습니다. 나는 forEach 가 더 빠르다 는 인상을 받았습니다 . 그러나 결국 나는 내가 틀렸다는 것을 깨달았습니다. 참고로 Java 1.5에 도입된 forEach 루프 (또는 개선된 for 루프 )는 반복자 또는 인덱스 변수를 완전히 숨김으로써 혼란과 오류 가능성을 제거합니다. for 와 forEach 의 유일한 실질적인 차이점은 인덱스된 객체의 경우 인덱스에 액세스할 수 없다는 것입니다.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 */
위의 바이트코드에서 다음을 볼 수 있습니다:
-
getfield 명령은 정수 변수를 얻는 데 사용됩니다.
-
List.iterator를 호출하여 반복자 인스턴스를 가져옵니다.
-
iterator.hasNext 를 호출하세요 . true를 반환하면 iterator.next 메서드를 호출해야 합니다 .
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);
}
}
결과는 다음과 같습니다. 보시다시피 for 루프의 성능은 forEach 루프 보다 좋습니다 . ArrayList 대신 LinkedList를 사용하면 LinkedList 의 forEach 성능이 더 우수하다는 것을 알 수 있습니다 . ArrayList는 내부적으로 배열을 사용하여 요소를 저장합니다. 배열은 메모리의 연속 영역이므로 시간 복잡도는 O(1)입니다. 이는 인덱스를 통해 데이터를 검색하기 때문입니다. LinkedList는 이중 연결리스트를 사용합니다. 순회를 구현하기 위해 for 루프를 사용할 때 마다 연결 리스트의 헤드 노드부터 시작하므로 시간 복잡도는 O(n*n)입니다.
Java 맵의 각 항목을 반복하는 8가지 효율적인 방법
출처: Medium 지난주에 한 인턴이 나에게 Java Map을 반복하는 방법을 물었습니다. 저는 아주 간단하기 때문에 이 질문에 대한 답은 항상 구글에 있다고 답했습니다. 얼마 후 그녀는 나에게 StackOverflow 페이지 주소를 보냈고 많은 사람들이 이 문제에 관심을 갖고 있다는 것을 알게 되었습니다. 따라서 나는 반복 문제에 대해 자세히 설명하고 이를 수행하는 몇 가지 방법을 여러분과 공유하기로 결정했습니다.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;
}
우리는 다음을 얻습니다:
GO TO FULL VERSION