Яка найефективніша бібліотека колекцій Java? [зачинено]


135

Яка найефективніша бібліотека колекцій Java?

Кілька років тому я багато працював на Java, і тоді було враження, що Trove - найкраща (найефективніша) реалізація Java Collections. Але коли я прочитав відповіді на запитання « Найкорисніші безкоштовні бібліотеки Java? », Я помітив, що троув майже не згадується. То яка бібліотека колекцій Java зараз найкраща?

ОНОВЛЕННЯ: Для уточнення я здебільшого хочу знати, яку бібліотеку використовувати, коли мені потрібно зберігати мільйони записів у хеш-таблиці тощо (потрібен невеликий час виконання та пам'яті).


Які ключі та значення в цій таблиці? Якщо вони не примітиви, що з нормальним HashMap тощо?
Джон Скіт

Для дуже великої карти вам може знадобитися зондувальна реалізація або навіть окреслена як таблиця бази даних.
Том Хотін - тайклін

1
Цікаво, що я не бачу тут згадки про Кольта, який згодом був підпорядкований Махуту.
smartnut007

4
Варто згадати дуже приємну колекційну бібліотеку - колекції GS (github.com/goldmansachs/gs-collections). У ньому є чудова документація та вичерпний набір змінних та незмінних колекцій
Пьотр Кочанський

Відповіді:


73

З огляду, схоже, що Trove - це просто бібліотека колекцій для примітивних типів - це не так, як це призначено для додавання багато функціональності над звичайними колекціями в JDK.

Особисто (і я упереджений) я люблю Guava (включаючи колишній проект Google Java Collections). Це робить різні завдання (включаючи колекції) набагато простішими, таким чином, принаймні, досить ефективними. Зважаючи на те, що операції збору рідко утворюють вузьке місце в моєму коді (на мій досвід), це "краще", ніж API колекцій, який може бути більш ефективним, але не робить мій код читабельним.

Зважаючи на те, що перекриття між Трове і Гуавою є майже нульовим, можливо, ви могли б уточнити, що ви насправді шукаєте з бібліотеки колекцій.


3
@Andreas: Не можу сказати, що я згоден. Не те, що це "той чи інший" сценарій - я використовую звичайні колекції (з такими помічниками, як клас Списки), а потім використовую Iterables тощо, коли мені потрібно. Використовуйте складність лише тоді, коли вона вам допоможе.
Джон Скіт

10
після прочитання власного коментаря через кілька місяців після широкого використання GC - я не згоден з моєю минулою думкою і повністю згоден з вашою. широко використовують допоміжні методи / класи, вони роблять більшу частину коду більш зрозумілою та безпечною.
Андреас Петерсон

1
@Andreas: Дякую, що повернувся і сказав так - Я радий почути, що GJC допомагає :)
Джон Скіт

2
Гей, Джон, колекції Google Java тепер є Guava . Можливо, ви захочете оновити свою публікацію для подальших посилань :)
Artur Czajka

1
Я працював над досить великими інформаційними проектами, де колекції були величезним вузьким місцем. Колекції Java надзвичайно неефективні (як пам'ять, так і швидкість), особливо якщо вони зберігають примітиви.
Джей Аскрен

104

Питання полягає в (зараз) про збереження безлічі даних, які можна представити, використовуючи примітивні типи, наприклад int, на карті. Деякі з відповідей тут, на мій погляд, дуже вводять в оману. Давайте розберемося, чому.

Я змінив орієнтир від trove, щоб виміряти як час виконання, так і споживання пам'яті. Я також додав PCJ до цього еталону, який є іншою бібліотекою колекцій для примітивних типів (я широко використовую цю). Офіційний бенчмарк не порівнює IntIntMaps з Java Collection Map<Integer, Integer>, ймовірно, зберігання Integersта зберігання intsз технічної точки зору не є однаковим. Але користувач може не перейматися цією технічною деталлю, він хоче ефективно зберігати дані, що представляються ints.

Спочатку відповідна частина коду:

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();
     }

Я припускаю, що дані надходять як примітивні ints, що здається здоровим. Але це передбачає штраф за час утиліти Java, через автоматичний бокс, який не є необхідним для примітивних систем колекцій.

Результати виконання (без gc()викликів, звичайно) на WinXP, jdk1.6.0_10:

                      100000 операцій пут 100000 містить операції 
колекції java 1938 мс 203 мс
trove 234 мс 125 мс
pcj 516 ms 94 мс

Хоча це вже може здатися драстичним, це не є причиною використання такої рамки.

Причина - продуктивність пам’яті. Результати для карти, що містить 100000 intзаписів:

Колекції java коливаються між 6644536 та 7168840 байтами
trove 1853296 байт
pcj 1866112 байт

Колекціям Java потрібно більше ніж утричі більше пам’яті порівняно з примітивними рамками колекції. Тобто ви можете зберігати в пам'яті втричі більше даних, не вдаючись до дискового вводу-виводу, що знижує продуктивність роботи на величини. І це має значення. Прочитайте високу масштабність, щоб з’ясувати, чому.

На мій досвід, велике споживання пам’яті є найбільшою проблемою продуктивності Java, що, звичайно, призводить до гіршої продуктивності роботи. Тут можуть справді допомогти примітивні рамки колекції.

Отже: Ні, java.util - це не відповідь. І "додавання функціональності" до колекцій Java - не сенс запитувати про ефективність. Також сучасні колекції JDK не «випереджають навіть спеціалізовані колекції Trove».

Відмова від відповідальності: Орієнтир тут ще далеко не повний, і він не є ідеальним. Це покликане загнати додому точку, яку я пережив у багатьох проектах. Примітивні колекції досить корисні, щоб переносити рибний API - якщо ви працюєте з великою кількістю даних.


3
Власне, я думаю, що ваша відповідь вводить в оману. Зберігання ints vs Integers дуже відрізняється, і, швидше за все, головна причина збільшення обсягу використання пам'яті. Я погоджуюся, що рамки збору сировинних типів можуть бути корисними, але це не робить trove або pcj "кращими", ніж java.util.
Jorn

22
Питання полягає в ефективному зберіганні даних int. Не про зберігання Integers. Для цього завдання trove / pcj ефективніші, як я намагався показати. Використання Integers нав'язує неефективність виконання та пам’яті. Оскільки java.util не дозволяє використовувати примітиви, це не найкращий вибір для цього завдання.
the.duckman

2
(для російської громади) тут іде ще один орієнтир: total-holywar.blogspot.com/2011/07/…
dma_k

Не впевнений, якщо ми не використовуємо int як ключ, а просто звичайний String. Яким буде результат для них верстака?
Кларк Бао

@ClarkBao (вибачте, що запізнюєтесь) Для зберігання будь-якого об’єкта як ключа буде використовуватися об'єкт hashCode(). Це дає вам intключ як ключ.
Матьє

47

Я знаю, що це стара публікація, і тут є багато відповідей. Але, відповіді вище є поверхневими та надто спрощеними з точки зору пропозиції бібліотеки. Немає жодної бібліотеки, яка б добре відповідала різним тестам, представленим тут. Єдиний висновок, який я отримую, це якщо ви дбаєте про продуктивність та пам'ять, а конкретно маєте справу з примітивними типами, то більш ніж варто переглянути альтернативи non jdk.

Ось більш ґрунтовний аналіз з точки зору механіки еталону та охоплених бібліотек. Це нитка у списку розробок mahout.

Розкриті бібліотеки є

  • HPPC
  • Trove
  • FastUtil
  • Махут (Кольт)
  • Колекції Java

Оновлення червня 2015 року : На жаль, оригінальні орієнтири більше не доступні, до того ж вони трохи застаріли. Ось досить останні (січень 2015 року) показники, зроблені кимось іншим. Він не такий комплексний, як і не має інтерактивних пошукових інструментів, як оригінальне посилання.


1
Дякую. Це було дуже корисно .. враховуючи важливість питання, важко повірити, що ніхто з інших відповідей (окрім the.duckman) насправді не відповідає на це запитання.
Декстер

20

Як зауважили інші коментатори, визначення "ефективного" кидає широку мережу. Однак про бібліотеку Javolution ніхто ще не згадав .

Деякі з основних моментів:

  • Класи Javolution - це швидко, дуже швидко (наприклад, Вставка / видалення тексту в O [Журнал (n)] замість O [n] для стандартного StringBuffer / StringBuilder).
  • Усі класи Javolution суворі в режимі реального часу та мають дуже детерміновану поведінку (в мікросекундному діапазоні). Крім того, на відміну від стандартної бібліотеки, Javolution є безпечним для RTSJ (без зіткнення пам’яті чи витоку пам’яті при використанні з розширенням Java в реальному часі).
  • Класи колекціонування в реальному часі Javolution (карта, список, таблиця та набір) можна використовувати замість більшості стандартних колекційних класів та надавати додаткову функціональність.
  • Колекції Javolution надають гарантії одночасності для спрощення реалізації паралельних алгоритмів.

Розподіл Javolution включає набір орієнтирів, щоб ви могли бачити, як вони співпадають з іншими бібліотеками / вбудованими колекціями.


16

Деякі колекції колекцій, які слід врахувати:

Я б передусім дотягнувся до бібліотеки колекцій JDK. Він охоплює найпоширеніші речі, які вам потрібно зробити, і, очевидно, вже доступні вам.

Google Collections - це, мабуть, найкраща високоякісна бібліотека поза JDK. Він широко використовується і добре підтримується.

Колекції Apache Commons старіші і трохи страждають від проблеми "занадто багато кухарів", але також має багато корисних речей.

Trove має дуже спеціалізовані колекції для таких випадків, як примітивні ключі / значення. В наші дні ми виявляємо, що на сучасних JDK та з колекціями Java 5+ та випадковими випадками використання колекції JDK виходять навіть із спеціалізованих колекцій Trove.

Якщо у вас є справді високі випадки використання паралельної валюти, вам обов'язково слід перевірити такі речі, як NonBlockingHashMap у високомасштабній області, яка є безблоковою реалізацією і може заступити на ConcurrentHashMap, якщо у вас є правильний випадок використання для цього.


7
"В наші дні ми виявляємо, що на сучасних JDK та з колекціями Java 5+ та випадками одночасного використання колекції JDK виходять навіть із спеціалізованих колекцій Trove." Введення в оману - я ніколи не бачив мікро-орієнтиру, де зберігання / вилучення примітивних типів у спеціалізованому класі примітивної колекції, як Trove, не перевершувало класи колекцій JDK як у використанні пам'яті, так і в процесі роботи процесора. Якщо ви використовуєте об'єкти, хоча (а не примітивні типи), то я б погодився з Алексом, бореться за колекцію impl - це не велика угода.
Ріяд Калла

2
Ця заява ґрунтувалася на важкому використанні в реальному світі (яке я прийму мікро-орієнтир будь-якого дня) різних колекційних записів, де раніше нам потрібна колекція Trove, але тепер ми змогли її витягнути. Пізні оновлення JDK 6 (приблизно в кінці 2009 року) фактично надали спеціальний код для загальних ключів карт, таких як Integer, які значно покращили деякі найпоширеніші використання.
Алекс Міллер

1
Алекс, я не сумніваюся у ваших конкретних випадках використання, коли витяг примітивних колекцій та збирання з колекціями JDK пройшов досить швидко, але махав рукою по колекційному пейзажу та говорив: "Все, що проходить, досить швидко! " не точний. Якщо я працюю над ігровим двигуном 2D, накладні витрати на бокс / розблокування моїх примітивних типів постійно помірно дорогі. Якщо я працюю над API REST, то ні, він, ймовірно, зовсім не може виміряти різні показники щодо набагато дорожчих операційних систем, таких як HTTP I / O. Я просто змушений був кількісно оцінити вашу посаду.
Ріяд Калла

4
Я не думаю, що хтось, хто читає це, не повинен слухати когось із нас. Вони повинні перевірити власний випадок використання та побачити, що має найкращі показники. Мої коментарі ґрунтуються на досить агресивних тестах ефективності моєї команди з різноманітними бібліотеками. YMMV.
Алекс Міллер

2
Я згоден з @Riyad. Я пишу високопродуктивний кінцевий набір автоматичних програм і впроваджую його як з Trove, так і з Java Collections Framework (останнє оновлення jdk 6). Trove перевершує великий час. На порядок в десятки разів краще як швидкість обчислення, так і споживання пам'яті.
Nico Huysamen

6

java.util

Вибачте за очевидну відповідь, але для більшості випадків колекції Java за замовчуванням більш ніж достатньо.


4
Для базового використання, так. Але я думаю, що в рамках не вистачає деяких основних і вдосконалених функцій (наприклад, незмінних колекцій, фільтрів, мультимап тощо), і саме тут (наприклад) з'являються колекції Google
Jorn,

1
Я думаю, що ця відповідь не відповідає суті. JCF, ймовірно, був приголомшливим у 2002 році, коли люди не використовували Java багато. На жаль, він не старіє, особливо в порівнянні з підтримкою колекцій з інших мов JVM.
Тед Пеннінгз

3
-1 Питання "найефективніше для зберігання int", і будь-який згаданий приклад кращий, ніж java.util
kommradHomer



3

ConcurrentHashMap , а також java.util.concurrentпакет слід згадати, якщо ви плануєте використовувати HashMap у кількох потоках. передбачається невеликий слід пам’яті, оскільки це частина стандартної Java.


3

Залежить від того, як ми визначимо "ефективний".

Кожна структура даних має власну поведінку Big-Oh щодо читання, запису, ітерації, сліду пам’яті тощо. Зв'язаний список в одній бібліотеці, ймовірно, буде таким же, як і в будь-якій іншій. І хеш-карта буде швидшою для читання O (1), ніж пов'язаний список O (n).

Але коли я прочитав відповіді на питання "Найкорисніші безкоштовні бібліотеки Java?" Я помітив, що тровель майже не згадується.

Це не здається "найбільш ефективним". Мені це звучить як "найпопулярніше".

Просто деякі відгуки - я ніколи про нього не чув, і не знаю нікого, хто ним користувався. Колекції, вбудовані в JDK, Google або Apache Commons, мені добре відомі.


3

Trove пропонує кілька переваг.

  • менший слід пам'яті, він не використовує об’єкти Map.Entry
  • ви можете використовувати хеш-стратегії замість клавіш для карт, це економить пам’ять і означає, що вам не потрібно визначати новий ключ кожен раз, коли ви хочете кешувати об’єкт у новому наборі його атрибутів
  • він має примітивні типи колекцій
  • думаю, він має певну форму внутрішнього ітератора

Однак це було зроблено багато для покращення колекцій jdk з моменту написання Trove.

Саме хеш-стратегії роблять це привабливим для мене, хоча ... Google for trove та читайте їхній огляд.


2

Якщо ви хочете зберегти мільйони записів у хеш-таблиці, велика ймовірність, що у вас виникнуть проблеми з пам'яттю. Це сталося зі мною, коли я намагався створити карту, наприклад, з 2,3 мільйона об'єктів String. Я поїхав з BerkeleyDB , який дуже зрілий і добре працює. У них є Java API, який обертає API Collections, тому ви можете легко створювати довільно великі карти з дуже невеликим слідом пам’яті. Доступ буде повільнішим (оскільки він зберігається на диску).

Наступне запитання : чи існує гідна (та ефективна), доглянута бібліотека для незмінних колекцій? Clojure має чудову підтримку для цього, і було б непогано мати щось подібне для Java.


1
Колекції Google додають незмінні колекції.
the.duckman
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.