Jaka jest najbardziej wydajna biblioteka kolekcji Java? [Zamknięte]

135

Jaka jest najbardziej wydajna biblioteka kolekcji Java?

Kilka lat temu dużo pracowałem nad Javą i odniosłem wtedy wrażenie, że znalezisko jest najlepszą (najbardziej wydajną) implementacją kolekcji Java. Kiedy jednak przeczytałem odpowiedzi na pytanie „ Najbardziej przydatne darmowe biblioteki Java? ”, Zauważyłem, że prawie się nie wspomina o tym skarbcu . Więc która biblioteka kolekcji Java jest teraz najlepsza?

AKTUALIZACJA: Aby wyjaśnić, przede wszystkim chcę wiedzieć, jakiej biblioteki użyć, gdy muszę przechowywać miliony wpisów w tabeli skrótów itp. (Potrzebuję niewielkiego czasu wykonywania i pamięci).

Szczery
źródło
Jakie są klucze i wartości w tej tabeli? Jeśli nie są prymitywami, co jest nie tak z normalną HashMapą itp.?
Jon Skeet,
W przypadku bardzo dużej mapy możesz potrzebować implementacji sondującej lub nawet wbudowanej, jak tabela bazy danych.
Tom Hawtin - tackline
1
Co ciekawe, nie widzę tutaj wzmianki o Colcie, który został następnie włączony do Mahouta.
smartnut007
4
Warto wspomnieć o bardzo ładnej bibliotece kolekcji - kolekcjach GS (github.com/goldmansachs/gs-collections). Posiada doskonałą dokumentację i wyczerpujący zestaw zmiennych i niezmiennych kolekcji
Piotr Kochański

Odpowiedzi:

73

Z oględzin wygląda na to, że Trove jest tylko biblioteką kolekcji dla typów prymitywnych - to nie jest tak, że ma dodawać wiele funkcji w stosunku do zwykłych kolekcji w JDK.

Osobiście (i jestem stronniczy) uwielbiam Guavę (w tym dawny projekt Google Java Collections). Znacznie ułatwia wykonywanie różnych zadań (w tym zbieranie) w sposób przynajmniej w miarę efektywny. Biorąc pod uwagę, że operacje zbierania rzadko tworzą wąskie gardło w moim kodzie (z mojego doświadczenia), jest to „lepsze” niż API kolekcji, które może być bardziej wydajne, ale nie czyni mojego kodu tak czytelnym.

Biorąc pod uwagę, że nakładanie się Trove i guawy jest prawie zerowe, być może mógłbyś wyjaśnić, czego faktycznie szukasz w bibliotece kolekcji.

Jon Skeet
źródło
3
@Andreas: Nie mogę powiedzieć, że się zgadzam. Nie chodzi o to, że jest to scenariusz „jeden lub drugi” - używam zwykłych kolekcji (z pomocnikami, takimi jak klasa Lists), a następnie używam Iterables itp., Kiedy muszę. Używaj złożoności tylko wtedy, gdy ci to pomaga.
Jon Skeet
10
po przeczytaniu własnego komentarza kilka miesięcy po intensywnym korzystaniu z GC - nie zgadzam się z moją wcześniejszą opinią iw pełni zgadzam się z Twoją. intensywnie używają metod / klas pomocniczych, dzięki czemu znaczna część kodu jest bardziej czytelna i bezpieczniejsza.
Andreas Petersson
1
@Andreas: Dziękuję, że wróciłeś i to powiedziałeś - cieszę się, że GJC pomaga :)
Jon Skeet
2
Hej, Jon, Google Java Collections to teraz Guava . Możesz zaktualizować swój post pod kątem przyszłych referencji :)
Artur Czajka
1
Pracowałem nad kilkoma projektami wymagającymi dużej ilości danych, w których kolekcje były ogromnym wąskim gardłem. Kolekcje Java są strasznie nieefektywne (zarówno pamięć, jak i szybkość), zwłaszcza jeśli przechowują prymitywy.
Jay Askren,
104

Pytanie dotyczy (teraz) przechowywania dużej ilości danych, które można przedstawić za pomocą typów prymitywnych, takich jak int Map. Niektóre odpowiedzi są moim zdaniem bardzo mylące. Zobaczmy, dlaczego.

Zmodyfikowałem test porównawczy z trove, aby mierzyć zarówno czas wykonywania, jak i zużycie pamięci. Dodałem również PCJ do tego benchmarku, który jest kolejną biblioteką kolekcji dla typów prymitywnych (używam tego intensywnie). „Oficjalny” test porównawczy skarbów nie porównuje IntIntMaps z Java Collection Map<Integer, Integer>, prawdopodobnie przechowywanie Integersi przechowywanie intsnie jest tym samym z technicznego punktu widzenia. Jednak użytkownik może nie przejmować się szczegółami technicznymi, za pomocą których chce przechowywać dane, które można przedstawićints efektywnie .

Najpierw odpowiednia część kodu:

new Operation() {

     private long usedMem() {
        System.gc();
        return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
     }

     // trove
     public void ours() {
        long mem = usedMem();
        TIntIntHashMap ours = new TIntIntHashMap(SET_SIZE);
        for ( int i = dataset.size(); i-- > 0; ) {
           ours.put(i, i);
        }
        mem = usedMem() - mem;
        System.err.println("trove " + mem + " bytes");
        ours.clear();
     }

     public void pcj() {
        long mem = usedMem();
        IntKeyIntMap map = new IntKeyIntOpenHashMap(SET_SIZE);
        for ( int i = dataset.size(); i-- > 0; ) {
           map.put(i, i);
        }
        mem = usedMem() - mem;
        System.err.println("pcj " + mem + " bytes");
        map.clear();
     }

     // java collections
     public void theirs() {
        long mem = usedMem();
        Map<Integer, Integer> map = new HashMap<Integer, Integer>(SET_SIZE);
        for ( int i = dataset.size(); i-- > 0; ) {
           map.put(i, i);
        }
        mem = usedMem() - mem;
        System.err.println("java " + mem + " bytes");
        map.clear();
     }

Zakładam, że dane są prymitywne ints , co wydaje się rozsądne. Ale to oznacza karę wykonawczą dla narzędzia java, ze względu na automatyczne boksowanie, które nie jest konieczne dla prymitywnych frameworków kolekcji.

Wyniki działania ( gc()oczywiście bez wywołań) na WinXP, jdk1.6.0_10:

                      100000 operacji sprzedaży 100000 zawiera operacje 
kolekcje java 1938 ms 203 ms
trove 234 ms 125 ms
PCJ 516 ms 94 ms

Chociaż może się to już wydawać drastyczne, nie jest to powód, aby korzystać z takiej struktury.

Powodem jest wydajność pamięci. Wyniki dla mapy zawierającej 100000 intwpisów:

kolekcje java oscylują między 6644536 a 7168840 bajtów
skarb 1853296 bajtów
pcj 1866112 bajtów

Kolekcje Java wymagają ponad trzykrotnie większej ilości pamięci niż prymitywne struktury kolekcji. Oznacza to, że można przechowywać w pamięci trzy razy więcej danych, bez uciekania się do operacji we / wy dysku, co znacznie obniża wydajność środowiska wykonawczego. I to ma znaczenie. Przeczytaj artykuł o wysokiej skalowalności, aby dowiedzieć się, dlaczego.

Z mojego doświadczenia wynika, że ​​duże zużycie pamięci jest największym problemem z wydajnością w Javie, co oczywiście skutkuje również gorszą wydajnością środowiska uruchomieniowego. Prymitywne struktury kolekcji mogą tu naprawdę pomóc.

A więc: nie, java.util nie jest odpowiedzią. A „dodawanie funkcjonalności” do kolekcji Java nie jest celem, gdy pytamy o wydajność. Również współczesne kolekcje JDK nie „wyprzedzają nawet wyspecjalizowanych kolekcji Trove”.

Zastrzeżenie: tutaj wzorzec jest daleki od ukończenia, ani też nie jest doskonały. Ma to na celu podkreślenie punktu, którego doświadczyłem w wielu projektach. Prymitywne kolekcje są wystarczająco przydatne, aby tolerować podejrzane API - jeśli pracujesz z dużą ilością danych.

the.duckman
źródło
3
Właściwie myślę, że twoja odpowiedź jest myląca. Przechowywanie liczb całkowitych i liczb całkowitych jest bardzo różne i najprawdopodobniej jest głównym powodem zwiększonego użycia pamięci. Zgadzam się, że framework do zbierania typów surowych może być przydatny, ale nie czyni to trove lub pcj "lepszym" niż java.util.
Jorn
22
Pytanie dotyczy efektywnego przechowywania danych int. Nie chodzi o przechowywanie liczb całkowitych. Do tego zadania trove / pcj są bardziej wydajne, jak próbowałem pokazać. Używanie liczb całkowitych narzuca nieefektywność czasu wykonywania i pamięci. Ponieważ java.util nie pozwala na używanie prymitywów, nie jest to najlepszy wybór do tego zadania.
the.duckman
2
(dla społeczności rosyjskiej) kolejny test porównawczy: total-holywar.blogspot.com/2011/07/…
dma_k
Nie jestem pewien, czy nie używamy int jako klucza, tylko zwykły String. Jaki będzie dla nich wynik warsztatu?
Clark Bao
@ClarkBao (przepraszam za spóźnienie) Przechowywanie dowolnego obiektu jako klucza spowoduje jego użycie hashCode(). Dostajesz intjako klucz.
Matthieu,
47

Wiem, że to stary post i jest tu mnóstwo odpowiedzi. Ale powyższe odpowiedzi są powierzchowne i zbyt uproszczone, jeśli chodzi o sugerowanie biblioteki. Nie ma jednej biblioteki, która radziłaby sobie dobrze z różnymi przedstawionymi tutaj testami porównawczymi. Jedynym wnioskiem, jaki wyciągam, jest to, że jeśli zależy ci na wydajności i pamięci, a konkretnie do czynienia z typami prymitywnymi, bardziej niż warto przyjrzeć się alternatywom innym niż jdk.

Oto bardziej rzetelna analiza, jeśli chodzi o mechanikę testów porównawczych i uwzględnione biblioteki. To jest wątek na liście deweloperów mahoutów.

Biblioteki objęte programem to

  • HPPC
  • Trove
  • FastUtil
  • Mahout (Colt)
  • Kolekcje Java

Aktualizacja czerwiec 2015 : Niestety, oryginalne testy porównawcze nie są już dostępne, a poza tym są nieco przestarzałe. Oto całkiem niedawne testy porównawcze (styczeń 2015) wykonane przez kogoś innego. Nie jest tak obszerny, ani nie ma interaktywnych narzędzi eksploracyjnych, jak oryginalny link.

smartnut007
źródło
1
Dziękuję Ci. To było bardzo pomocne… biorąc pod uwagę wagę pytania, trudno uwierzyć, że żadna z pozostałych odpowiedzi (poza tą zakłamaną) faktycznie nie odpowiada na to pytanie.
Dexter,
20

Jak zauważyli inni komentatorzy, definicja „efektywnego” rzuca szeroką sieć. Jednak nikt jeszcze nie wspomniał o bibliotece Javolution .

Niektóre z najważniejszych:

  • Klasy Javolution są szybkie, bardzo szybkie (np. Wstawianie / usuwanie tekstu w O [Log (n)] zamiast O [n] dla standardowego StringBuffer / StringBuilder).
  • Wszystkie klasy Javolution są trudne do spełnienia w czasie rzeczywistym i mają wysoce deterministyczne zachowanie (w zakresie mikrosekund). Ponadto (w przeciwieństwie do biblioteki standardowej), Javolution jest bezpieczny dla RTSJ (brak konfliktów pamięci lub wycieków pamięci, gdy jest używany z rozszerzeniem Java Real-Time).
  • Klasy kolekcji w czasie rzeczywistym Javolution (mapa, lista, tabela i zestaw) mogą być używane zamiast większości standardowych klas kolekcji i zapewniają dodatkową funkcjonalność.
  • Kolekcje Javolution zapewniają gwarancje współbieżności, aby ułatwić implementację algorytmów równoległych.

Dystrybucja Javolution zawiera zestaw testów porównawczych, dzięki czemu można zobaczyć, jak wypadają na tle innych bibliotek / wbudowanych kolekcji.

sstock
źródło
16

Niektóre biblioteki kolekcji do rozważenia:

Przede wszystkim sięgnąłbym po bibliotekę JDK. Obejmuje najczęstsze rzeczy, które musisz zrobić, i jest oczywiście już dostępny.

Kolekcje Google to prawdopodobnie najlepsza biblioteka wysokiej jakości poza JDK. Jest mocno używany i dobrze obsługiwany.

Kolekcja Apache Commons jest starsza i trochę cierpi z powodu problemu „zbyt wielu kucharzy”, ale zawiera również wiele przydatnych rzeczy.

Trove ma bardzo wyspecjalizowane zbiory przypadków, takich jak prymitywne klucze / wartości. Obecnie okazuje się, że na nowoczesnych JDK oraz w przypadku kolekcji Java 5+ i jednoczesnych przypadków użycia, kolekcje JDK przewyższają nawet wyspecjalizowane kolekcje Trove.

Jeśli masz naprawdę wysokie przypadki użycia współbieżności, zdecydowanie powinieneś sprawdzić rzeczy takie jak NonBlockingHashMap w bibliotece o dużej skali, która jest implementacją wolną od blokad i może tupnąć na ConcurrentHashMap, jeśli masz do tego odpowiedni przypadek użycia.

Alex Miller
źródło
7
„Obecnie okazuje się, że na nowoczesnych JDK oraz w przypadku kolekcji Java 5+ i jednoczesnych przypadków użycia, kolekcje JDK przewyższają nawet wyspecjalizowane kolekcje Trove”. Mylące - nigdy nie widziałem mikro-testu porównawczego, w którym przechowywanie / pobieranie typów pierwotnych w wyspecjalizowanej klasie kolekcji elementów pierwotnych, takiej jak Trove, nie przewyższało klas kolekcji JDK zarówno pod względem wykorzystania pamięci, jak i czasu procesora. Jeśli jednak używasz obiektów (a nie typów prymitywnych), zgodziłbym się z Alexem, że martwienie się o kolekcję impl nie jest aż tak trudne.
Riyad Kalla
2
To stwierdzenie było oparte na intensywnym używaniu w świecie rzeczywistym (które przejmę mikro-benchmark każdego dnia) różnych implantów kolekcji, w przypadku których wcześniej potrzebowaliśmy kolekcji Trove, ale teraz byliśmy w stanie ją wyciągnąć. Późne aktualizacje JDK 6 (około końca 2009 r.) Faktycznie dostarczyły niestandardowy kod dla popularnych kluczy map, takich jak Integer, które znacznie poprawiły niektóre z najczęstszych zastosowań.
Alex Miller
1
Alex, nie wątpię w twoich konkretnych przypadkach użycia, że ​​wyciąganie prymitywnych kolekcji i przechodzenie z kolekcjami JDK było wystarczająco szybkie, ale machając ręką nad krajobrazem, czyli kolekcjami, i mówiąc: „Wszyscy, co mijasz, jest wystarczająco szybko! " nie jest dokładne. Jeśli pracuję na silniku gry 2D, koszty związane z boksowaniem / rozpakowywaniem moich prymitywnych typów są wymiernie drogie. Jeśli pracuję nad REST API, to nie, prawdopodobnie nie powoduje to żadnej mierzalnej różnicy w stosunku do znacznie droższych operacji, takich jak HTTP I / O. Po prostu czułem się zmuszony do ilościowego określenia twojego postu.
Riyad Kalla
4
Nie sądzę, żeby ktokolwiek to czytał, powinien słuchać któregokolwiek z nas. Powinni przetestować swój własny przypadek użycia i zobaczyć, co ma najlepszą wydajność. Moje komentarze opierają się na dość agresywnych testach wydajnościowych przeprowadzonych przez mój zespół w różnych bibliotekach. YMMV.
Alex Miller
2
Zgadzam się z @Riyad. Piszę pakiet automatów skończonych o wysokiej wydajności i zaimplementowałem go zarówno w Trove, jak i w Java Collections Framework (najnowsza aktualizacja jdk 6). Trove osiąga lepsze wyniki. W rzędzie dziesiątek razy lepsze zarówno pod względem szybkości obliczeń, jak i zużycia pamięci.
Nico Huysamen
6

java.util

Przepraszamy za oczywistą odpowiedź, ale w przypadku większości zastosowań domyślne kolekcje Java są więcej niż wystarczające.

Yuval Adam
źródło
4
Do podstawowych zastosowań tak. Myślę jednak, że w ramach tej platformy brakuje niektórych podstawowych i zaawansowanych funkcji (takich jak niezmienne kolekcje, filtry, multimapy itp.) I właśnie tam (na przykład) pojawia się Google Kolekcje
Jorn,
1
Myślę, że ta odpowiedź mija się z celem. JCF był prawdopodobnie niesamowity w 2002 roku, kiedy ludzie nie używali Javy zbyt często. Niestety nie zestarzał się dobrze, zwłaszcza w porównaniu z obsługą kolekcji z innych języków JVM.
Ted Pennings
3
-1 Pytanie jest "najbardziej wydajne do przechowywania int", a każdy wymieniony przykład jest lepszy niż java.util
kommradHomer
6

Aby przechowywać miliony obiektów Stringna mapie, spójrz na http://code.google.com/p/flatmap

akuhn
źródło
3
+1 Czy możesz przedstawić, jak to się ulepszyło?
Clark Bao
1
Gdzieś w internecie powinny znajdować się posty na blogu autora flatmap.
akuhn
4

Jestem twórcą happy-collections z happy-collections na source-forge

  1. Kolekcje oparte na wydarzeniach
  2. Niemodyfikowalne
  3. SortedList
  4. Pamięć podręczna
Andreas Hollmann
źródło
3

ConcurrentHashMap, a także java.util.concurrentpakiet, należy wspomnieć, jeśli planujesz używać HashMap w wielu wątkach. zakłada się niewielkie zużycie pamięci, ponieważ jest to część standardowego oprogramowania java.

Andreas Petersson
źródło
3

Zależy od tego, jak zdefiniujemy „efektywny”.

Każda struktura danych ma swoje własne zachowanie Big-Oh do czytania, pisania, iteracji, śladu pamięci itp. Połączona lista w jednej bibliotece prawdopodobnie będzie taka sama jak każda inna. Mapa skrótów będzie szybsza do odczytu O (1) niż połączona lista O (n).

Ale kiedy czytam odpowiedzi na pytanie „Najbardziej przydatne darmowe biblioteki Java?” Zauważyłem, że prawie się nie wspomina o tym skarbcu.

To nie brzmi jak „najbardziej wydajne”. Dla mnie brzmi to jak „najpopularniejszy”.

Tylko garść opinii - nigdy o tym nie słyszałem i nie znam nikogo, kto z niego korzystał. Kolekcje wbudowane w JDK, Google czy Apache Commons są mi dobrze znane.

duffymo
źródło
3

Trove ma kilka zalet.

  • mniejsze zużycie pamięci, nie używa obiektów Map.Entry
  • możesz użyć strategii haszowania zamiast kluczy do map, oszczędza to pamięć i oznacza, że ​​nie musisz definiować nowego klucza za każdym razem, gdy chcesz buforować obiekt w nowym zestawie jego atrybutów
  • ma prymitywne typy kolekcji
  • myślę, że ma jakąś formę wewnętrznego iteratora

To powiedziawszy, wiele zrobiono, aby ulepszyć kolekcje jdk od czasu napisania trove.

To właśnie strategie haszowania sprawiają, że jest to dla mnie atrakcyjne ... Szukaj w Google i przeczytaj ich przegląd.

duffymo
źródło
2

Jeśli chcesz przechowywać miliony rekordów w tabeli skrótów, prawdopodobnie napotkasz problemy z pamięcią. Zdarzyło mi się to na przykład, gdy próbowałem stworzyć mapę z 2,3 milionami obiektów typu String. Poszedłem z BerkeleyDB , który jest bardzo dojrzały i działa dobrze. Mają Java API, które otacza Kolekcje API, dzięki czemu można łatwo tworzyć dowolnie duże mapy z bardzo małym śladem pamięci. Dostęp będzie jednak wolniejszy (ponieważ jest przechowywany na dysku).

Pytanie uzupełniające : czy istnieje porządna (i wydajna), dobrze utrzymana biblioteka na niezmienne zbiory? Clojure ma do tego doskonałe wsparcie i byłoby miło mieć coś podobnego dla Javy.

fred-o
źródło
1
Kolekcje Google dodają niezmienne Kolekcje.
the.duckman