JavaRush /Blog Java /Random-VI /Nghỉ giải lao #137. Vòng lặp For hoặc Foreach - cái nào n...

Nghỉ giải lao #137. Vòng lặp For hoặc Foreach - cái nào nhanh hơn trong Java? 8 cách hiệu quả để lặp qua từng mục trong bản đồ Java

Xuất bản trong nhóm

Vòng lặp For hoặc Foreach - cái nào nhanh hơn trong Java?

Nguồn: Medium Khi tôi đang tìm việc cách đây vài năm, một trong những câu hỏi tôi được hỏi trong một cuộc phỏng vấn là liệu chúng ta có nên lặp lại ArrayList bằng cách sử dụng for hay forEach không ? Nghỉ giải lao #137.  Vòng lặp For hoặc Foreach - cái nào nhanh hơn trong Java?  8 cách hiệu quả để lặp qua từng mục trong bản đồ Java - 1Cuộc tranh luận về sự khác biệt trong sở thích giữa forEachfor đã diễn ra từ lâu. Tôi có ấn tượng rằng forEach nhanh hơn. Nhưng cuối cùng tôi nhận ra rằng mình đã sai. Cho bạn biết, vòng lặp forEach (hoặc vòng lặp for được cải tiến ) được giới thiệu trong Java 1.5 giúp loại bỏ sự lộn xộn và khả năng xảy ra lỗi bằng cách ẩn hoàn toàn biến lặp hoặc biến chỉ mục. Tôi tin rằng sự khác biệt thực tế duy nhất giữa forforEach là trong trường hợp các đối tượng được lập chỉ mục, chúng tôi không có quyền truy cập vào chỉ mục.
for(int i = 0; i < mylist.length; i++) {
 if(i < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Tuy nhiên, chúng ta có thể tạo một biến chỉ mục riêng có kiểu int bằng cách sử dụng forEach . Ví dụ:
int index = -1;
for(int myint : mylist) {
 index++;
 if(index < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Hãy viết một lớp đơn giản có phương thức foreachTest() lặp qua một danh sách bằng cách sử dụng forEach .
import java.util.List;

public class ForEachTest {
	List<Integer> intList;

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

        }
    }
}
Khi chúng tôi biên dịch lớp này, trình biên dịch sẽ chuyển đổi nội bộ mã thành một triển khai lặp. Tôi đã dịch ngược mã đã biên dịch bằng cách chạy 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 */
Từ mã byte ở trên, chúng ta thấy:
  1. Lệnh getfield được sử dụng để lấy các biến số nguyên.

  2. Gọi List.iterator để lấy một phiên bản iterator.

  3. Gọi iterator.hasNext . Nếu nó trả về true thì phương thức iterator.next sẽ được gọi .

Hãy chạy thử nghiệm hiệu suất. Trong phương thức IterateListTest chính , tôi đã tạo một danh sách và lặp qua nó bằng các vòng lặp 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);
	}
}
Và đây là kết quả: Nghỉ giải lao #137.  Vòng lặp For hoặc Foreach - cái nào nhanh hơn trong Java?  8 cách hiệu quả để lặp qua từng mục trong bản đồ Java - 2Như chúng ta có thể thấy, hiệu suất của vòng lặp for tốt hơn vòng lặp forEach . Nếu bạn sử dụng LinkedList thay vì ArrayList , bạn có thể thấy rằng hiệu suất của forEach sẽ tốt hơn đối với LinkedList . ArrayList sử dụng mảng nội bộ để lưu trữ các phần tử. Vì mảng là các vùng bộ nhớ liền kề nhau nên độ phức tạp về thời gian là O(1). Điều này là do dữ liệu được lấy thông qua các chỉ mục. LinkedList sử dụng danh sách liên kết đôi. Khi chúng ta sử dụng vòng lặp for để triển khai truyền tải, nó sẽ bắt đầu từ nút đầu của danh sách liên kết mỗi lần, do đó độ phức tạp về thời gian là O(n*n).

8 cách hiệu quả để lặp qua từng mục trong bản đồ Java

Nguồn: Medium Tuần trước, một thực tập sinh đã hỏi tôi cách lặp lại Bản đồ Java. Tôi trả lời rằng vì nó rất đơn giản nên câu trả lời cho câu hỏi này luôn có trên Google. Sau một thời gian, cô ấy gửi cho tôi địa chỉ của trang trên StackOverflow và hóa ra có rất nhiều người đang chú ý đến vấn đề này. Vì vậy, tôi quyết định tập trung vào vấn đề lặp lại và chia sẻ một số cách để thực hiện điều đó với bạn. Nghỉ giải lao #137.  Vòng lặp For hoặc Foreach - cái nào nhanh hơn trong Java?  8 cách hiệu quả để lặp qua từng mục trong bản đồ Java - 3

1. Sử dụng iterator và 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. Sử dụng foreach và 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. Sử dụng foreach từ Java 8

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

4. Sử dụng keySet và foreach

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

5. Sử dụng keySet và iterator

@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. Sử dụng for và 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. Sử dụng API luồng Java 8

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

8. Sử dụng song song API Java 8 Stream

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

So sánh từng phương pháp về tốc độ:

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;
}
Chúng tôi nhận được: Nghỉ giải lao #137.  Vòng lặp For hoặc Foreach - cái nào nhanh hơn trong Java?  8 cách hiệu quả để lặp qua từng mục trong bản đồ Java - 4Nghỉ giải lao #137.  Vòng lặp For hoặc Foreach - cái nào nhanh hơn trong Java?  8 cách hiệu quả để lặp qua từng mục trong bản đồ Java - 5Nghỉ giải lao #137.  Vòng lặp For hoặc Foreach - cái nào nhanh hơn trong Java?  8 cách hiệu quả để lặp qua từng mục trong bản đồ Java - 6

Phần kết luận

Từ việc so sánh dữ liệu, chúng tôi biết rằng phương pháp 6 mất nhiều thời gian nhất và phương pháp 8 mất nhiều thời gian nhất khi số nhỏ, nhưng mất ít thời gian nhất khi số có ý nghĩa vì phương thức 8 được thực thi đồng thời. Điều thú vị là thứ tự thực hiện kiểm tra luôn là for -> while -> foreach/stream và tôi không biết tại sao :(
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION