JavaRush /Blog Java /Random-MS /Coffee break #137. Untuk gelung atau Foreach - yang manak...

Coffee break #137. Untuk gelung atau Foreach - yang manakah lebih pantas di Jawa? 8 Cara Cekap untuk Menggulung Setiap Entri dalam Peta Java

Diterbitkan dalam kumpulan

Untuk gelung atau Foreach - yang manakah lebih pantas di Jawa?

Sumber: Sederhana Apabila saya sedang mencari pekerjaan beberapa tahun yang lalu, salah satu soalan yang ditanya kepada saya dalam temu bual ialah sama ada kita perlu mengulangi ArrayList menggunakan for atau forEach ? Coffee break #137.  Untuk gelung atau Foreach - yang manakah lebih pantas di Jawa?  8 Cara Cekap untuk Menggulung Setiap Entri dalam Peta Java - 1Perdebatan tentang perbezaan pilihan antara forEach dan for telah lama wujud. Saya mendapat tanggapan bahawa forEach lebih pantas. Tetapi akhirnya saya sedar bahawa saya salah. FYI, gelung forEach (atau dipertingkatkan untuk gelung ) yang diperkenalkan dalam Java 1.5 menghapuskan kekacauan dan kemungkinan ralat dengan menyembunyikan lelaran atau pembolehubah indeks sepenuhnya. Saya percaya satu-satunya perbezaan praktikal antara for dan forEach ialah dalam kes objek diindeks, kami tidak mempunyai akses kepada indeks.
for(int i = 0; i < mylist.length; i++) {
 if(i < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Walau bagaimanapun, kita boleh mencipta pembolehubah indeks jenis int yang berasingan menggunakan forEach . Sebagai contoh:
int index = -1;
for(int myint : mylist) {
 index++;
 if(index < 5) {
 //do something
 } else {
 //do other stuff
 }
}
Mari tulis kelas mudah yang mempunyai kaedah foreachTest() yang berulang melalui senarai menggunakan forEach .
import java.util.List;

public class ForEachTest {
	List<Integer> intList;

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

        }
    }
}
Apabila kami menyusun kelas ini, pengkompil secara dalaman menukar kod menjadi pelaksanaan lelaran. Saya menyahkompilasi kod yang disusun dengan menjalankan 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 */
Daripada bytecode di atas kita lihat:
  1. Perintah getfield digunakan untuk mendapatkan pembolehubah integer.

  2. Panggil List.iterator untuk mendapatkan contoh iterator.

  3. Panggil iterator.hasNext . Jika ia kembali benar, kaedah iterator.next hendaklah dipanggil .

Mari jalankan ujian prestasi. Dalam kaedah IterateListTest utama , saya mencipta senarai dan mengulanginya menggunakan gelung for dan forEach .
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);
	}
}
Dan inilah hasilnya: Coffee break #137.  Untuk gelung atau Foreach - yang manakah lebih pantas di Jawa?  8 Cara Cekap untuk Mengulang Melalui Setiap Entri dalam Peta Java - 2Seperti yang kita dapat lihat, prestasi gelung for adalah lebih baik daripada gelung forEach . Jika anda menggunakan LinkedList dan bukannya ArrayList , anda boleh melihat bahawa prestasi forEach adalah lebih baik untuk LinkedList . ArrayList secara dalaman menggunakan tatasusunan untuk menyimpan elemen. Oleh kerana tatasusunan ialah kawasan ingatan yang bersebelahan, kerumitan masa ialah O(1). Ini kerana data diperoleh melalui indeks. LinkedList menggunakan senarai berganda. Apabila kita menggunakan gelung for untuk melaksanakan traversal, ia bermula dari nod kepala senarai terpaut setiap kali, jadi kerumitan masa ialah O(n*n).

8 Cara Cekap untuk Menggulung Setiap Entri dalam Peta Java

Sumber: Sederhana Minggu lepas, seorang pelatih bertanya kepada saya cara untuk mengulang Peta Java. Saya menjawab bahawa kerana ia sangat mudah, jawapan kepada soalan ini sentiasa di Google. Selepas beberapa lama, dia menghantar saya alamat halaman di StackOverflow, dan ternyata sebilangan besar orang memberi perhatian kepada masalah ini. Oleh itu, saya memutuskan untuk membincangkan isu lelaran dan berkongsi beberapa cara untuk melakukannya dengan anda. Coffee break #137.  Untuk gelung atau Foreach - yang manakah lebih pantas di Jawa?  8 Cara Cekap untuk Mengulang Melalui Setiap Entri dalam Peta Java - 3

1. Menggunakan iterator dan 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. Menggunakan foreach dan 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. Menggunakan foreach dari 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. Menggunakan keySet dan foreach

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

5. Menggunakan keySet dan 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. Menggunakan untuk dan 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. Menggunakan Java 8 Stream API

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

8. Penggunaan selari Java 8 Stream API

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

Perbandingan setiap kaedah dari segi kelajuan:

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;
}
Kita mendapatkan: Coffee break #137.  Untuk gelung atau Foreach - yang manakah lebih pantas di Jawa?  8 Cara Cekap untuk Menggulung Setiap Entri dalam Peta Java - 4Coffee break #137.  Untuk gelung atau Foreach - yang manakah lebih pantas di Jawa?  8 Cara Cekap untuk Mengulang Melalui Setiap Entri dalam Peta Java - 5Coffee break #137.  Untuk gelung atau Foreach - yang manakah lebih pantas di Jawa?  8 Cara Cekap untuk Mengulang Melalui Setiap Entri dalam Peta Java - 6

Kesimpulan

Daripada membandingkan data, kami mengetahui bahawa kaedah 6 mengambil masa paling banyak dan kaedah 8 mengambil masa paling banyak apabila nombor itu kecil, tetapi mengambil masa paling sedikit apabila nombor itu penting kerana kaedah 8 dilaksanakan secara serentak. Perkara yang menarik ialah perintah pelaksanaan ujian sentiasa untuk -> sementara -> foreach/strim dan saya tidak tahu mengapa :(
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION