Zakładając, że mam ArrayList
ArrayList<MyClass> myList;
I chcę zadzwonić do Array, czy istnieje powód, dla którego warto użyć wydajności
MyClass[] arr = myList.toArray(new MyClass[myList.size()]);
nad
MyClass[] arr = myList.toArray(new MyClass[0]);
?
Wolę drugi styl, ponieważ jest mniej szczegółowy i założyłem, że kompilator upewni się, że pusta tablica tak naprawdę nie zostanie utworzona, ale zastanawiałem się, czy to prawda.
Oczywiście w 99% przypadków nie robi to różnicy w taki czy inny sposób, ale chciałbym zachować spójny styl między moim normalnym kodem i zoptymalizowanymi wewnętrznymi pętlami ...
java
performance
coding-style
itsadok
źródło
źródło
Odpowiedzi:
Wbrew intuicji najszybsza wersja w Hotspot 8 to:
Przeprowadziłem mikro-test porównawczy przy użyciu jmh, wyniki i kod są poniżej, pokazując, że wersja z pustą tablicą konsekwentnie przewyższa wersję z predefiniowaną tablicą. Zwróć uwagę, że jeśli możesz ponownie użyć istniejącej tablicy o odpowiednim rozmiarze, wynik może być inny.
Wyniki testów porównawczych (wynik w mikrosekundach, mniejszy = lepszy):
W celach informacyjnych kod:
Podobne wyniki, pełną analizę i dyskusję znajdziesz w poście na blogu Arrays of Wisdom of the Ancients . Podsumowując: kompilator JVM i JIT zawiera kilka optymalizacji, które umożliwiają tanie tworzenie i inicjowanie nowej tablicy o prawidłowym rozmiarze, a tych optymalizacji nie można użyć, jeśli samodzielnie utworzysz tablicę.
źródło
MyClass[] arr = myList.stream().toArray(MyClass[]::new);
... które, jak sądzę, byłyby wolniejsze. Chciałbym również zobaczyć testy porównawcze różnicy z deklaracją tablicy. Jak w różnicy między:MyClass[] arr = new MyClass[myList.size()]; arr = myList.toArray(arr);
aMyClass[] arr = myList.toArray(new MyClass[myList.size()]);
... czy nie powinno być żadnej różnicy? Myślę, że te dwa są problemem, który jest pozatoArray
funkcjami. Ale hej! Nie sądziłem, że poznam inne skomplikowane różnice.toArray
bezpośrednie wywołanie (im mniejszy rozmiar, tym większy względny narzut)Od ArrayList w Javie 5 tablica będzie już wypełniona, jeśli ma odpowiedni rozmiar (lub jest większa). w konsekwencji
utworzy jeden obiekt tablicy, wypełni go i zwróci do "arr". Z drugiej strony
utworzy dwie tablice. Drugi to tablica MyClass o długości 0. Jest więc tworzenie obiektu dla obiektu, który zostanie natychmiast wyrzucony. O ile sugeruje kod źródłowy, kompilator / JIT nie może zoptymalizować tego, aby nie został utworzony. Ponadto użycie obiektu o zerowej długości skutkuje rzutowaniem (rzutami) w ramach metody toArray () -.
Zobacz źródło ArrayList.toArray ():
Użyj pierwszej metody, aby utworzyć tylko jeden obiekt i uniknąć (niejawnych, ale kosztownych) rzutów.
źródło
new Myclass[0]
jest szybszy: shipilev.net/blog/2016/arrays-wisdom-ancientsZ inspekcji JetBrains Intellij Idea:
źródło
Nowoczesne maszyny JVM optymalizują w tym przypadku konstrukcję tablicy odblaskowej, więc różnica w wydajności jest niewielka. Dwukrotne nazwanie kolekcji w takim standardowym kodzie nie jest dobrym pomysłem, więc unikałbym pierwszej metody. Kolejną zaletą drugiej jest to, że działa ona z kolekcjami synchronizowanymi i współbieżnymi. Jeśli chcesz dokonać optymalizacji, użyj ponownie pustej tablicy (puste tablice są niezmienne i mogą być współużytkowane) lub użyj profilera (!).
źródło
private static final MyClass[] EMPTY_MY_CLASS_ARRAY = new MyClass[0]
nie zapobiega zwracana tablica z skonstruowany przez odbicie, ale nie zapobiega dodatkowa tablica jest co każdorazowo wykonana.MyClass[] arr = myList.stream().toArray(MyClass[]::new);
Czy to pomogłoby lub zaszkodziło w przypadku zsynchronizowanych i współbieżnych kolekcji. I dlaczego? Proszę.toArray sprawdza, czy przekazana tablica ma właściwy rozmiar (czyli wystarczająco dużą, aby zmieścić elementy z Twojej listy), a jeśli tak, to używa. W konsekwencji, jeśli rozmiar tablicy pod warunkiem, że jest mniejszy niż wymagany, nowa tablica zostanie utworzona odruchowo.
W twoim przypadku tablica o rozmiarze zero jest niezmienna, więc można ją bezpiecznie podnieść do statycznej zmiennej końcowej, co może sprawić, że kod będzie trochę czystszy, co pozwoli uniknąć tworzenia tablicy przy każdym wywołaniu. Nowa tablica i tak zostanie utworzona wewnątrz metody, więc jest to optymalizacja czytelności.
Prawdopodobnie szybszą wersją jest przekazywanie tablicy o odpowiednim rozmiarze, ale jeśli nie możesz udowodnić, że ten kod stanowi wąskie gardło wydajności, preferuj czytelność od wydajności w czasie wykonywania, dopóki nie zostanie udowodnione, że jest inaczej.
źródło
Pierwszy przypadek jest bardziej skuteczny.
Dzieje się tak, ponieważ w drugim przypadku:
Środowisko wykonawcze faktycznie tworzy pustą tablicę (o rozmiarze zerowym), a następnie wewnątrz metody toArray tworzy kolejną tablicę, aby dopasować rzeczywiste dane. To tworzenie odbywa się za pomocą odbicia przy użyciu następującego kodu (pobranego z jdk1.5.0_10):
Korzystając z pierwszego formularza, unikasz tworzenia drugiej tablicy, a także unikasz kodu odbicia.
źródło
Drugi jest nieznacznie bardziej czytelny, ale poprawa jest tak mała, że nie jest tego warta. Pierwsza metoda jest szybsza i nie ma żadnych wad w czasie wykonywania, więc właśnie tego używam. Ale piszę to w drugi sposób, ponieważ pisanie jest szybsze. Wtedy moje IDE oznacza to jako ostrzeżenie i proponuje naprawienie tego. Jednym naciśnięciem klawisza konwertuje kod z drugiego typu na pierwszy.
źródło
Użycie „toArray” z tablicą o odpowiednim rozmiarze będzie działać lepiej, ponieważ alternatywa utworzy najpierw tablicę o rozmiarze zerowym, a następnie tablicę o odpowiednim rozmiarze. Jednak, jak mówisz, różnica będzie prawdopodobnie nieistotna.
Należy również zauważyć, że kompilator javac nie przeprowadza żadnej optymalizacji. Obecnie wszystkie optymalizacje są wykonywane przez kompilatory JIT / HotSpot w czasie wykonywania. Nie znam żadnych optymalizacji dotyczących „toArray” w żadnej JVM.
Odpowiedź na twoje pytanie jest zatem w dużej mierze kwestią stylu, ale ze względu na spójność powinna stanowić część wszelkich standardów kodowania, których się przestrzegasz (udokumentowanych lub innych).
źródło
przykładowy kod dla liczby całkowitej:
źródło