Какая библиотека Java Collections наиболее эффективна? [закрыто]

135

Какая библиотека Java Collections наиболее эффективна?

Несколько лет назад я много занимался Java, и у меня тогда сложилось впечатление, что trove - лучшая (самая эффективная) реализация Java Collections. Но когда я прочитал ответы на вопрос « Самые полезные бесплатные библиотеки Java? », Я заметил, что эта книга почти не упоминается. Так какая библиотека Java Collections сейчас лучше?

ОБНОВЛЕНИЕ: Чтобы уточнить, я в основном хочу знать, какую библиотеку использовать, когда мне нужно хранить миллионы записей в хэш-таблице и т. Д. (Требуется небольшой объем времени выполнения и объем памяти).

Фрэнк
источник
Какие ключи и значения в этой таблице? Если они не примитивы, что не так с обычным HashMap и т. Д.?
Джон Скит
Для очень большой карты вам может потребоваться пробная реализация, или даже встроенная, как таблица базы данных.
Том Хотин - tackline
1
Интересно, что я не вижу здесь упоминания о Кольте, который впоследствии был включен в Махоут.
smartnut007
4
Стоит упомянуть очень хорошую коллекцию библиотек - коллекций GS (github.com/goldmansachs/gs-collections). Он имеет отличную документацию и исчерпывающий набор изменчивых и неизменных коллекций
Петр Кочанский

Ответы:

73

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

Лично (и я предвзято) я люблю Guava (включая бывший проект Google Java Collections). Это значительно облегчает выполнение различных задач (в том числе коллекций), по крайней мере, достаточно эффективно. Учитывая, что операции сбора редко образуют узкое место в моем коде (по моему опыту), это «лучше», чем API сбора данных, который может быть более эффективным, но не делает мой код более читабельным.

Учитывая, что перекрытие между Trove и Guava в значительной степени равно нулю, возможно, вы могли бы уточнить, что вы на самом деле ищете из библиотеки коллекций.

Джон Скит
источник
3
@ Андреас: Не могу сказать, что я согласен. Не то чтобы это «тот или другой» сценарий - я использую обычные коллекции (с такими помощниками, как класс Lists), а затем использую Iterables и т. Д., Когда мне это нужно. Используйте сложность только тогда, когда она вам помогает.
Джон Скит
10
после прочтения моего собственного комментария через несколько месяцев после широкого использования GC - я не согласен с моим прошлым мнением и полностью согласен с вашим. широко используйте вспомогательные методы / классы, они делают большую часть кода более читабельной и более безопасной.
Андреас Петерссон
1
@Andreas: Спасибо, что вернулись и сказали так - я рад слышать, что GJC помогает :)
Джон Скит
2
Привет, Джон, Google Java Collections теперь Guava . Вы можете обновить свой пост для будущих ссылок :)
Артур Czajka
1
Я работал над несколькими проектами с интенсивным использованием данных, где коллекции были огромным узким местом. Коллекции Java ужасно неэффективны (как память, так и скорость), особенно если они хранят примитивы.
Джей Аскрен
104

Вопрос (сейчас) о хранении большого количества данных, которые могут быть представлены с использованием примитивных типов, например int, на карте. Некоторые ответы здесь очень вводят в заблуждение, на мой взгляд. Посмотрим почему.

Я изменил эталонный тест Trove для измерения времени выполнения и потребления памяти. Я также добавил PCJ к этому бенчмарку, который является еще одной библиотекой коллекций для примитивных типов (я широко ее использую). «Официальный» тест производительности не сравнивает IntIntMaps с коллекцией Java 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 мс
Трос 234 мс 125 мс
pcj 516 мс 94 мс

Хотя это может показаться существенным, но это не причина для использования такой основы.

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

коллекции java колеблются между 6644536 и 7168840 байтами
трое 1853296 байт
pcj 1866112 байт

Коллекции Java требуют более чем в три раза больше памяти по сравнению с примитивными платформами сбора. Т.е. вы можете хранить в три раза больше данных в памяти, не прибегая к дисковым операциям ввода-вывода, которые снижают быстродействие во времени. И это имеет значение. Читайте подробности о том, почему.

По моему опыту, высокое потребление памяти является самой большой проблемой производительности Java, что, конечно, также приводит к снижению производительности во время выполнения. Примитивные рамки коллекции могут действительно помочь здесь.

Итак: нет, java.util не является ответом. И «добавление функциональности» в коллекции Java - не главное, когда спрашивают об эффективности. Также современные коллекции JDK не "превосходят даже специализированные коллекции Trove".

Отказ от ответственности: эталонный тест здесь далек от завершения и не идеален. Он предназначен для того, чтобы показать, что я испытал во многих проектах. Примитивные коллекции достаточно полезны, чтобы терпеть подозрительные API - если вы работаете с большим количеством данных.

the.duckman
источник
3
На самом деле, я думаю, что ваш ответ вводит в заблуждение. Хранение целых и целых чисел сильно отличается и, скорее всего, является основной причиной увеличения использования памяти. Я согласен, что полезная структура сбора типов может быть полезной, но она не делает trove или pcj "лучше", чем java.util.
Йорн
22
Речь идет об эффективном хранении данных int. Не о хранении целых чисел. Для этой задачи Trove / PCJ являются более эффективными, как я пытался показать. Использование Целых чисел налагает нехватку времени выполнения и памяти. Поскольку java.util не позволяет использовать примитивы, это не лучший выбор для этой задачи.
the.duckman
2
(для русской общины) здесь идет еще один тест: total-holywar.blogspot.com/2011/07/…
dma_k
Не уверен, что мы не используем int в качестве ключа, просто обычную строку. Каким будет для них результат рабочего места?
Кларк Бао
@ClarkBao (извините за опоздание) Хранение любого объекта в качестве ключа будет использовать объект hashCode(). Это получает вас intкак ключ.
Матье
47

Я знаю, что это старый пост, и здесь есть тонна ответов. Но, ответы выше являются поверхностными и слишком упрощенными с точки зрения предложения библиотеки. Нет ни одной библиотеки, которая бы справлялась с различными тестами, представленными здесь. Единственный вывод, который я могу сделать, - это если вы заботитесь о производительности и памяти, особенно о примитивных типах, то стоит взглянуть на альтернативы, отличные от jdk.

Вот более подробный анализ с точки зрения механики тестирования и рассматриваемых библиотек. Это тема в списке разработчиков mahout.

Библиотеки покрыты

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

Обновление июнь 2015 : К сожалению, оригинальные тесты больше не доступны и, кроме того, они немного устарели. Вот сравнительно недавние (январь 2015 г.) тесты, выполненные кем-то другим. Он не такой всеобъемлющий и не имеет интерактивных поисковых инструментов, как исходная ссылка.

smartnut007
источник
1
Спасибо. Это было очень полезно ... учитывая важность вопроса, трудно поверить, что ни один из других ответов (кроме the.duckman) фактически не отвечает на этот вопрос.
Декстер
20

Как отметили другие комментаторы, определение «эффективный» создает широкую сеть. Однако никто еще не упомянул библиотеку Javolution .

Некоторые из основных моментов:

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

Дистрибутив Javolution включает набор тестов, чтобы вы могли увидеть, как они складываются с другими библиотеками / встроенными коллекциями.

sstock
источник
16

Некоторые коллекции libs для рассмотрения:

В первую очередь я хотел бы обратиться к библиотеке коллекций JDK. Он охватывает наиболее распространенные вещи, которые вам нужно сделать, и, очевидно, уже доступен для вас.

Google Collections, вероятно, лучшая высококачественная библиотека за пределами JDK. Он активно используется и хорошо поддерживается.

Коллекции Apache Commons старше и немного страдают от проблемы «слишком много поваров», но также содержат много полезных вещей.

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

Если у вас действительно высокий уровень использования параллелизма, вы обязательно должны проверить такие вещи, как NonBlockingHashMap в высокопроизводительной библиотеке lib, которая является реализацией без блокировок и может растоптать ConcurrentHashMap, если у вас есть подходящий вариант использования.

Алекс Миллер
источник
7
«В наши дни мы обнаруживаем, что в современных JDK и с коллекциями Java 5+ и параллельными вариантами использования коллекции JDK превосходят даже специализированные коллекции Trove». Вводящее в заблуждение - я никогда не видел микробенчмарка, в котором хранение / извлечение примитивных типов в специализированном классе сбора примитивов, таком как Trove, не превзошло классы сбора JDK как по использованию памяти, так и по времени ЦП. Хотя, если вы используете объекты (а не примитивные типы), то я бы согласился с Алексом, что беспокойство по поводу коллекции не так уж сложно.
Рияд Калла
2
Это утверждение было основано на интенсивном использовании в реальных условиях (которое я буду принимать за микро-тестирование в любой день) различных коллекционных примеров, где нам прежде требовалась коллекция Trove, но теперь мы могли ее извлечь. Поздние обновления JDK 6 (около конца 2009 года) фактически предоставили пользовательский код для общих ключей карты, таких как Integer, которые значительно улучшили некоторые из наиболее распространенных применений.
Алекс Миллер
1
Алекс, я не сомневаюсь в твоих конкретных случаях использования, что вытащить примитивные коллекции и пойти с коллекциями JDK было достаточно быстро, но махать рукой по пейзажу, который является коллекциями, и говорить: «Все, что вы проходите, это достаточно быстро! " не точно Если я работаю над движком 2D-игр, накладные расходы на бокс / распаковку моих примитивных типов постоянно измеримо дороги. Если я работаю над REST API, то нет, это, вероятно, вообще не делает измеримое отличие от более дорогих операций, таких как HTTP I / O. Я просто был вынужден количественно оценить ваш пост, вот и все.
Рияд Калла
4
Я не думаю, что кто-то, читающий это, должен слушать кого-либо из нас. Они должны протестировать свой собственный вариант использования и посмотреть, что имеет лучшую производительность. Мои комментарии основаны на довольно агрессивных тестах производительности моей команды с различными библиотеками. YMMV.
Алекс Миллер
2
Я согласен с @Riyad. Я пишу высокопроизводительный конечный набор автоматов и реализовал его с помощью Trove и Java Collections Framework (последнее обновление jdk 6). Троув выигрывает у большого времени. В десятки раз лучше как по скорости вычислений, так и по потреблению памяти.
Нико Хюйсамен
6

java.util

Извините за очевидный ответ, но для большинства случаев стандартных коллекций Java более чем достаточно.

Ювал Адам
источник
4
Для базового использования, да. Но я думаю, что фреймворк упускает некоторые базовые и расширенные функции (такие как неизменяемые коллекции, фильтры, мультикарты и т. Д.), И именно здесь (например) приходит Google Collections
Jorn
1
Я думаю, что этот ответ не имеет смысла. JCF, вероятно, был замечательным в 2002 году, когда люди не очень часто использовали Java. К сожалению, он не очень хорошо состарился, особенно по сравнению с поддержкой коллекций из других языков JVM.
Тед Пеннингс
3
-1 Вопрос «наиболее эффективен для хранения int», и любой упомянутый пример лучше, чем java.util
kommradHomer
6

Чтобы хранить миллионы Stringна карте, взгляните на http://code.google.com/p/flatmap.

akuhn
источник
3
+1 Можете представить, как это улучшилось?
Кларк Бао
1
Должны быть сообщения в блоге автора flatmap где-нибудь в Интернете.
akuhn
3

java.util.concurrentСледует упомянуть ConcurrentHashMap, а также пакет, если вы планируете использовать HashMap в нескольких потоках. предполагается небольшой объем памяти, так как это является частью стандартного Java.

Андреас Петерссон
источник
3

Зависит от того, как мы определяем «эффективный».

Каждая структура данных имеет свое поведение Big-Oh для чтения, записи, итерации, использования памяти и т. Д. Связанный список в одной библиотеке, вероятно, будет таким же, как и любой другой. И хэш-карта будет быстрее для чтения O (1), чем связанный список O (n).

Но когда я читаю ответы на вопрос «Самые полезные бесплатные библиотеки Java?» Я заметил, что это едва упоминается.

Это не звучит как «самый эффективный». Это звучит как «самый популярный» для меня.

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

duffymo
источник
3

Trove предлагает несколько преимуществ.

  • меньший объем памяти, он не использовал объекты Map.Entry
  • вы можете использовать хеш-стратегии вместо ключей для карт, это экономит память и означает, что вам не нужно определять новый ключ каждый раз, когда вы хотите кэшировать объект на новом наборе его атрибутов
  • у него есть примитивные типы коллекций
  • думаю, что есть какая-то форма внутреннего итератора

Тем не менее, много было сделано для улучшения коллекций jdk с тех пор, как был написан trove.

Это стратегии хеширования, которые делают его привлекательным для меня, хотя ... Google для поиска и прочитать их обзор.

duffymo
источник
2

Если вы хотите хранить миллионы записей в хеш-таблице, есть вероятность, что у вас возникнут проблемы с памятью. Это случилось со мной, например, когда я попытался создать карту с 2,3 миллионами объектов String. Я пошел с BerkeleyDB , который очень зрелый и хорошо работает. У них есть Java API, который упаковывает API Коллекций, так что вы можете легко создавать карты произвольно больших размеров с очень небольшим объемом памяти. Хотя доступ будет медленнее (так как он хранится на диске).

Дополнительный вопрос : есть ли приличная (и эффективная), ухоженная библиотека для неизменных коллекций? Clojure имеет отличную поддержку для этого, и было бы неплохо иметь что-то подобное для Java.

Фрэд-о
источник
1
Коллекции Google добавляет неизменные Коллекции.
the.duckman