JavaRush /Blog Java /Random-PL /Przerwa kawowa #137. Pętla For lub Foreach - która z nich...

Przerwa kawowa #137. Pętla For lub Foreach - która z nich jest szybsza w Javie? 8 skutecznych sposobów przeglądania każdego wpisu na mapie Java

Opublikowano w grupie Random-PL

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 ? Przerwa kawowa #137.  Pętla For lub Foreach - która z nich jest szybsza w Javie?  8 skutecznych sposobów przeglądania każdego wpisu w mapie Java — 1Debata 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:
  1. Polecenie getfield służy do uzyskiwania zmiennych całkowitych.

  2. Wywołaj List.iterator , aby uzyskać instancję iteratora.

  3. Wywołaj iterator.hasNext . Jeśli zwróci wartość true, należy wywołać metodę iterator.next .

Przeprowadźmy test wydajności. W głównej metodzie IterateListTest utworzyłem listę i iterowałem po niej, używając pętli for i 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);
	}
}
A oto wynik: Przerwa kawowa #137.  Pętla For lub Foreach - która z nich jest szybsza w Javie?  8 skutecznych sposobów iteracji po każdym wpisie w mapie Java — 2jak 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ć. Przerwa kawowa #137.  Pętla For lub Foreach - która z nich jest szybsza w Javie?  8 skutecznych sposobów iteracji po każdym wpisie w mapie Java — 3

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: Przerwa kawowa #137.  Pętla For lub Foreach - która z nich jest szybsza w Javie?  8 skutecznych sposobów przeglądania każdego wpisu w mapie Java — 4Przerwa kawowa #137.  Pętla For lub Foreach - która z nich jest szybsza w Javie?  8 skutecznych sposobów iteracji po każdym wpisie w mapie Java — 5Przerwa kawowa #137.  Pętla For lub Foreach - która z nich jest szybsza w Javie?  8 skutecznych sposobów iteracji po każdym wpisie w mapie Java — 6

Wniosek

Z porównania danych dowiedzieliśmy się, że metoda 6 zajmuje najwięcej czasu, metoda 8 zajmuje najwięcej czasu, gdy liczba jest mała, natomiast najmniej czasu, gdy liczba jest znacząca, ponieważ metoda 8 jest wykonywana jednocześnie. Ciekawostką jest to, że kolejność wykonania testu jest zawsze następująca: -> while -> foreach/stream i nie wiem dlaczego :(
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION