Jaka jest różnica wydajności między następującymi dwiema pętlami?
for (Object o: objectArrayList) {
o.DoSomething();
}
i
for (int i=0; i<objectArrayList.size(); i++) {
objectArrayList.get(i).DoSomething();
}
java
performance
for-loop
eflles
źródło
źródło
Odpowiedzi:
Z pozycji 46 w „ Skutecznej Javie” Joshua Blocha:
źródło
Wszystkie te pętle robią dokładnie to samo, chcę je tylko pokazać, zanim wrzucę moje dwa centy.
Po pierwsze, klasyczny sposób zapętlania listy:
Po drugie, preferowany sposób, ponieważ jest mniej podatny na błędy (ile razy TY robiłeś „ups, mieszałeś zmienne ij w tych pętlach w pętlach”?).
Po trzecie, mikrooptymalizowana pętla:
Teraz rzeczywiste dwa centy: przynajmniej kiedy je testowałem, trzeci był najszybszy, licząc milisekundy, ile czasu zajęło każdemu typowi pętli z prostą operacją powtarzaną kilka milionów razy - przy użyciu Java 5 z jre1.6u10 na Windows na wypadek, gdyby ktoś był zainteresowany.
Chociaż przynajmniej wydaje się, że trzeci jest najszybszy, naprawdę powinieneś zadać sobie pytanie, czy chcesz ryzykować wdrożenie tej optymalizacji wizjera w dowolnym miejscu w kodzie pętli, ponieważ z tego, co widziałem, rzeczywiste zapętlenie nie jest Zazwyczaj najbardziej czasochłonna część każdego prawdziwego programu (a może po prostu pracuję na złym polu, kto wie). I tak jak wspomniałem w pretekstie dla pętli Java dla każdej (niektóre nazywają ją pętlą Iterator, a inne jako pętlą for-in ), rzadziej trafisz na ten konkretny głupi błąd podczas korzystania z niej. I zanim zastanowimy się, jak to może być nawet szybsze niż inne, pamiętaj, że javac wcale nie optymalizuje kodu bajtowego (no cóż, prawie w ogóle), po prostu go kompiluje.
Jeśli jednak interesujesz się mikrooptymalizacją i / lub twoje oprogramowanie korzysta z wielu pętli rekurencyjnych, możesz być zainteresowany trzecim typem pętli. Pamiętaj tylko, aby dobrze przetestować oprogramowanie zarówno przed, jak i po zmianie pętli for, musisz mieć to dziwne, mikrooptymalizowane.
źródło
get(int)
, drugi używaIterator
. Zastanów się,LinkedList
gdzie wydajnośćfor(int i=0;i<strings.size();i++) { /* do something using strings.get(i) */ }
jest znacznie gorsza, ponieważ działaget(int)
n razy.Pętla for-each powinna być ogólnie preferowana. Podejście „get” może być wolniejsze, jeśli używana implementacja listy nie obsługuje dostępu losowego. Na przykład, jeśli użyto LinkedList, poniosłbyś koszty przejścia, podczas gdy podejście dla każdego używa iteratora, który śledzi jego pozycję na liście. Więcej informacji na temat niuansów w pętli For-each .
Myślę, że artykuł jest teraz tutaj: nowa lokalizacja
Link pokazany tutaj był martwy.
źródło
Wpływ na wydajność jest w większości nieznaczny, ale nie jest zerowy. Jeśli spojrzysz na JavaDoc
RandomAccess
interfejsu:Pętla for-each używa wersji z iteratorem, więc
ArrayList
na przykład pętla for-each nie jest najszybsza.źródło
Iterable
s, ale nie tablic. W przypadku tablic stosowana jest pętla for ze zmienną indeksu. Informacje na ten temat znajdują się w JLS .Niestety wydaje się, że istnieje różnica.
Jeśli spojrzysz na wygenerowany kod bajtów dla obu rodzajów pętli, są one różne.
Oto przykład z kodu źródłowego Log4j.
W /log4j-api/src/main/java/org/apache/logging/log4j/MarkerManager.java mamy statyczną klasę wewnętrzną o nazwie Log4jMarker, która definiuje:
Ze standardową pętlą:
Z dla każdego:
O co chodzi z TYM Oracle?
Próbowałem tego z Javą 7 i 8 w systemie Windows 7.
źródło
Zawsze lepiej jest używać iteratora zamiast indeksowania. Wynika to z faktu, że iterator jest najprawdopodobniej zoptymalizowany pod kątem implementacji listy, podczas gdy indeksowanie (wywołanie get) może nie być. Na przykład LinkedList jest listą, ale indeksowanie jej elementów będzie wolniejsze niż iteracja za pomocą iteratora.
źródło
foreach sprawia, że intencja twojego kodu jest jaśniejsza i jest to zwykle preferowane w porównaniu do bardzo niewielkiej poprawy prędkości - jeśli w ogóle.
Ilekroć widzę indeksowaną pętlę, muszę ją trochę dłużej analizować, aby upewnić się, że działa tak , jak jej się wydaje. Np. Czy zaczyna się od zera, czy zawiera lub wyklucza punkt końcowy itp.?
Większość mojego czasu wydaje się spędzać na czytaniu kodu (który napisałem lub napisał ktoś inny), a przejrzystość jest prawie zawsze ważniejsza niż wydajność. Obecnie łatwo jest odrzucić wydajność, ponieważ Hotspot wykonuje tak niesamowitą robotę.
źródło
Poniższy kod:
Daje następujące dane wyjściowe w moim systemie:
Używam Ubuntu 12.10 alpha z aktualizacją OracleJDK 1.7 6.
Ogólnie HotSpot optymalizuje wiele pośrednich i prostych operacji reduntant, więc ogólnie nie powinieneś się nimi przejmować, chyba że jest ich dużo w sekwencji lub są mocno zagnieżdżone.
Z drugiej strony indeksowane get na LinkedList jest znacznie wolniejsze niż wywoływanie next na iteratorze dla LinkedList, dzięki czemu można uniknąć tego spadku wydajności, zachowując czytelność podczas korzystania z iteratorów (jawnie lub pośrednio w każdej pętli).
źródło
Nawet w przypadku czegoś takiego jak ArrayList lub Vector, gdzie „get” jest prostym wyszukiwaniem tablicy, druga pętla nadal ma dodatkowy narzut, którego nie ma pierwsza. Spodziewałbym się, że będzie trochę wolniejszy niż pierwszy.
źródło
Jedynym sposobem, aby się upewnić, jest przeprowadzenie testu porównawczego, a nawet to nie jest tak proste, jak mogłoby się wydawać . Kompilator JIT może wykonywać bardzo nieoczekiwane rzeczy w kodzie.
źródło
Oto krótka analiza różnicy przedstawionej przez zespół programistów Androida:
https://www.youtube.com/watch?v=MZOf3pOAM6A
Powoduje to, że nie ma różnicy, w bardzo ograniczonych środowiskach o bardzo dużych list może być zauważalna różnica. W testach pętla dla każdej pętli trwała dwa razy dłużej. Jednak ich testy przekroczyły liczbę 400 000 liczb całkowitych. Rzeczywista różnica na element w tablicy wyniosła 6 mikrosekund . Nie testowałem i nie powiedzieli, ale spodziewam się, że różnica będzie nieco większa przy użyciu obiektów niż prymitywów, ale nawet jeśli nie budujesz kodu biblioteki, w którym nie masz pojęcia o skali tego, o co zostaniesz zapytany powtarzam, myślę, że różnica nie jest warta podkreślenia.
źródło
Według nazwy zmiennej
objectArrayList
zakładam, że jest to instancjajava.util.ArrayList
. W takim przypadku różnica wydajności byłaby niezauważalna.Z drugiej strony, jeśli jest to przypadek
java.util.LinkedList
, drugie podejście będzie znacznie wolniejsze niżList#get(int)
jest operacją O (n).Dlatego zawsze preferowane jest pierwsze podejście, chyba że indeks jest potrzebny logice w pętli.
źródło
Oba robią to samo, ale dla łatwego i bezpiecznego programowania dla każdego, istnieją możliwości podatności na błędy w 2. sposobie użycia.
źródło
To dziwne, że nikt nie wspomniał o oczywistości - foreach przydziela pamięć (w formie iteratora), podczas gdy normalna pętla for nie przydziela żadnej pamięci. W przypadku gier na Androida jest to problem, ponieważ oznacza to, że moduł czyszczenia pamięci będzie działał okresowo. W grze nie chcesz, aby śmieciarz działał ... NIGDY. Więc nie używaj pętli foreach w swojej metodzie rysowania (lub renderowania).
źródło
Zaakceptowana odpowiedź odpowiada na pytanie, oprócz wyjątkowego przypadku ArrayList ...
Ponieważ większość programistów polega na ArrayList (przynajmniej tak mi się wydaje)
Jestem więc zobowiązany do podania poprawnej odpowiedzi tutaj.
Prosto z dokumentacji programisty: -
Ulepszoną pętlę for (czasami nazywaną również pętlą „dla każdego”) można używać w kolekcjach, które implementują interfejs Iterable oraz w tablicach. W przypadku kolekcji przydzielany jest iterator do wykonywania wywołań interfejsów do hasNext () i next (). W przypadku ArrayList ręcznie odliczana pętla jest około 3 razy szybsza (z JIT lub bez JIT), ale w innych kolekcjach rozszerzona składnia pętli for będzie dokładnie równoważna jawnemu użyciu iteratora.
Istnieje kilka wariantów iteracji po tablicy:
zero () jest najwolniejsze, ponieważ JIT nie może jeszcze zoptymalizować kosztu uzyskania długości tablicy raz dla każdej iteracji przez pętlę.
jeden () jest szybszy. Wyciąga wszystko do zmiennych lokalnych, unikając wyszukiwania. Tylko długość macierzy zapewnia korzyści wydajnościowe.
two () jest najszybszy dla urządzeń bez JIT i nie do odróżnienia od one () dla urządzeń z JIT. Wykorzystuje ulepszoną składnię pętli for wprowadzoną w wersji 1.5 języka programowania Java.
Tak więc powinieneś domyślnie używać rozszerzonej pętli for, ale weź pod uwagę ręcznie odliczaną pętlę dla krytycznej wydajności iteracji ArrayList.
źródło
Tak,
for-each
wariant jest szybszy niż normalnieindex-based-for-loop
.for-each
warianty zastosowańiterator
. Zatem ruch jest szybszy niż normalnafor
pętla oparta na indeksie.Wynika to z tego, że
iterator
są zoptymalizowane pod kątem ruchu, ponieważ wskazuje na tuż przed następnym elementem i tuż za poprzednim elementem . Jednym z powodówindex-based-for-loop
tego, że jest powolny, jest to, że musi on obliczać i przesuwać się do pozycji elementu za każdym razem, gdy nie ma tegoiterator
.źródło
źródło