For loop atau Foreach - mana yang lebih cepat di Java?
Sumber: Medium Ketika saya sedang mencari pekerjaan beberapa tahun yang lalu, salah satu pertanyaan yang ditanyakan kepada saya dalam sebuah wawancara adalah apakah kita harus mengulangi penggunaan ArrayList for atau forEach ? Perdebatan tentang perbedaan preferensi antara forEach dan for telah ada sejak lama. Saya mendapat kesan bahwa forEach lebih cepat. Namun pada akhirnya saya menyadari bahwa saya salah. FYI, forEach loop (atau for loop yang ditingkatkan ) yang diperkenalkan di Java 1.5 menghilangkan kekacauan dan kemungkinan kesalahan dengan menyembunyikan variabel iterator atau indeks seluruhnya. Saya percaya satu-satunya perbedaan praktis antara for dan forEach adalah bahwa dalam kasus objek yang diindeks, kami tidak memiliki akses ke indeks.for(int i = 0; i < mylist.length; i++) {
if(i < 5) {
//do something
} else {
//do other stuff
}
}
Namun, kita dapat membuat variabel indeks terpisah bertipe int menggunakan forEach . Misalnya:
int index = -1;
for(int myint : mylist) {
index++;
if(index < 5) {
//do something
} else {
//do other stuff
}
}
Mari kita menulis kelas sederhana yang memiliki metode foreachTest() yang melakukan iterasi melalui daftar menggunakan forEach .
import java.util.List;
public class ForEachTest {
List<Integer> intList;
public void foreachTest(){
for(Integer i : intList){
}
}
}
Saat kita mengkompilasi kelas ini, kompiler secara internal mengubah kode menjadi implementasi iterator. Saya mendekompilasi kode yang dikompilasi 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 */
Dari bytecode di atas kita melihat:
-
Perintah getfield digunakan untuk mendapatkan variabel integer.
-
Panggil List.iterator untuk mendapatkan instance iterator.
-
Hubungi iterator.hasNext . Jika hasilnya benar, metode iterator.next harus dipanggil .
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: Seperti yang bisa kita lihat, kinerja perulangan for lebih baik daripada perulangan forEach . Jika Anda menggunakan LinkedList daripada ArrayList , Anda dapat melihat bahwa kinerja forEach lebih baik untuk LinkedList . ArrayList secara internal menggunakan array untuk menyimpan elemen. Karena array adalah wilayah memori yang berdekatan, kompleksitas waktunya adalah O(1). Ini karena data diambil melalui indeks. LinkedList menggunakan daftar tertaut ganda. Saat kita menggunakan perulangan for untuk mengimplementasikan traversal, perulangan tersebut dimulai dari node utama daftar tertaut setiap kali, sehingga kompleksitas waktunya adalah O(n*n).
8 Cara Efisien untuk Mengulang Setiap Entri di Peta Java
Sumber: Medium Minggu lalu, seorang pekerja magang bertanya kepada saya bagaimana cara mengulangi Java Map. Saya menjawab karena sangat sederhana, jawaban atas pertanyaan ini selalu ada di Google. Setelah beberapa waktu, dia mengirimi saya alamat halaman di StackOverflow, dan ternyata banyak sekali orang yang memperhatikan masalah ini. Oleh karena itu, saya memutuskan untuk memikirkan masalah iterasi dan berbagi dengan Anda beberapa cara untuk melakukannya.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 for 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 API Aliran Java 8
@Test
public void test7_UsingJava8StreamApi(){
System. out .println(map.entrySet().stream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}
8. Penggunaan Java 8 Stream API secara paralel
@Test
public void test8_UsingJava8StreamApiParallel(){
System. out .println(map.entrySet().parallelStream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}
Perbandingan masing-masing metode dalam hal kecepatan:
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:
GO TO FULL VERSION