Pętla For lub Foreach - która z nich jest szybsza w Javie?
Źródło: Medium Kiedy kilka lat temu szukałem pracy, jedno z pytań, jakie zadano mi w rozmowie kwalifikacyjnej, brzmiało: czy powinniśmy iterować po ArrayList , używając for lub forEach ? Debata na temat różnic w preferencjach pomiędzy forEach i for toczy się już od dłuższego czasu. Miałem wrażenie, że forEach jest szybszy. Ale w końcu zrozumiałem, że się myliłem. Dla twojej informacji, pętla forEach (lub ulepszona pętla for ) wprowadzona w Javie 1.5 eliminuje bałagan i możliwość wystąpienia błędu poprzez całkowite ukrycie iteratora lub zmiennej indeksowej. Uważam, że jedyną praktyczną różnicą między for i forEach jest to, że w przypadku indeksowanych obiektów nie mamy dostępu do indeksu.for(int i = 0; i < mylist.length; i++) {
if(i < 5) {
//do something
} else {
//do other stuff
}
}
Możemy jednak utworzyć osobną zmienną indeksującą typu int za pomocą forEach . Na przykład:
int index = -1;
for(int myint : mylist) {
index++;
if(index < 5) {
//do something
} else {
//do other stuff
}
}
Napiszmy prostą klasę zawierającą metodę foreachTest() iterującą po liście za pomocą forEach .
import java.util.List;
public class ForEachTest {
List<Integer> intList;
public void foreachTest(){
for(Integer i : intList){
}
}
}
Kiedy kompilujemy tę klasę, kompilator wewnętrznie konwertuje kod na implementację iteratora. Zdekompilowałem skompilowany kod, uruchamiając 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 */
Z powyższego kodu bajtowego widzimy:
-
Polecenie getfield służy do uzyskiwania zmiennych całkowitych.
-
Wywołaj List.iterator , aby uzyskać instancję iteratora.
-
Wywołaj iterator.hasNext . Jeśli zwróci wartość true, należy wywołać metodę 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);
}
}
A oto wynik: jak widzimy, działanie pętli for jest lepsze niż pętli forEach . Jeśli użyjesz LinkedList zamiast ArrayList , możesz zobaczyć, że forEach wydajność jest lepsza w przypadku LinkedList . ArrayList wewnętrznie używa tablic do przechowywania elementów. Ponieważ tablice są sąsiadującymi obszarami pamięci, złożoność czasowa wynosi O(1). Dzieje się tak, ponieważ dane są pobierane za pomocą indeksów. LinkedList używa listy podwójnie połączonej. Kiedy używamy pętli for do implementacji przechodzenia, zaczyna się ona za każdym razem od węzła głównego połączonej listy, więc złożoność czasowa wynosi O(n*n).
8 skutecznych sposobów przeglądania każdego wpisu na mapie Java
Źródło: Medium W zeszłym tygodniu stażysta zapytał mnie, jak iterować mapę Java. Odpowiedziałem, że skoro jest to bardzo proste, to odpowiedź na to pytanie zawsze znajdziesz w Google. Po pewnym czasie przesłała mi adres strony na StackOverflow i okazało się, że na ten problem zwraca uwagę ogromna liczba osób. Dlatego postanowiłem zatrzymać się nad kwestią iteracji i podzielić się z Wami kilkoma sposobami, jak to zrobić.1. Korzystanie z iteratora i 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. Korzystanie z foreach i 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. Używanie foreach z 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. Używanie zestawu kluczy i fore
@Test
public void test4_UsingKeySetAndForEach(){
long i = 0;
for (Integer key : map.keySet()) {
i += key + map.get(key);
}
System.out.println(i);
}
5. Korzystanie z zestawu kluczy i iteratora
@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. Używanie for i 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. Korzystanie z interfejsu API strumienia Java 8
@Test
public void test7_UsingJava8StreamApi(){
System. out .println(map.entrySet().stream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}
8. Równoległe wykorzystanie Java 8 Stream API
@Test
public void test8_UsingJava8StreamApiParallel(){
System. out .println(map.entrySet().parallelStream().mapToLong(e -> e.getKey() + e.getValue()).sum());
}
Porównanie każdej metody pod względem szybkości:
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;
}
Otrzymujemy:
GO TO FULL VERSION