Перейти к основному содержимому

Руководство по FastUtil

· 5 мин. чтения

1. Введение

В этом уроке мы рассмотрим библиотеку FastUtil .

Во- первых, мы напишем несколько примеров коллекций, зависящих от типа.

Затем мы проанализируем производительность, благодаря которой FastUtil получил свое название.

Наконец, давайте взглянем на утилиты FastUtil BigArray . ``

2. Особенности

Библиотека FastUtil Java стремится расширить платформу коллекций Java. Он предоставляет карты, наборы, списки и очереди для конкретных типов с меньшим объемом памяти и быстрым доступом и вставкой. FastUtil также предоставляет набор утилит для работы с большими (64-битными) массивами, множествами и списками и манипулирования ими.

Библиотека также включает в себя множество практичных классов ввода/вывода для двоичных и текстовых файлов.

Его последний выпуск, FastUtil 8, также выпустил множество функций для конкретных типов , расширяющих функциональные интерфейсы JDK .

2.1. Скорость

Во многих случаях реализации FastUtil являются самыми быстрыми из доступных. Авторы даже предоставили собственный подробный отчет о тестах , сравнивая его с аналогичными библиотеками, включая HPPC и Trove.

В этом руководстве мы попытаемся определить наши собственные тесты с помощью Java Microbench Harness (JMH) .

3. Полноразмерная зависимость

Помимо обычной зависимости JUnit , в этом руководстве мы будем использовать зависимости FastUtils и JMH .

Нам понадобятся следующие зависимости в нашем файле pom.xml :

<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>fastutil</artifactId>
<version>8.2.2</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.33</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.33</version>
<scope>test</scope>
</dependency>

Или для пользователей Gradle:

testCompile group: 'org.openjdk.jmh', name: 'jmh-core', version: '1.19'
testCompile group: 'org.openjdk.jmh', name: 'jmh-generator-annprocess', version: '1.19'
compile group: 'it.unimi.dsi', name: 'fastutil', version: '8.2.2'

3.1. Индивидуальный JAR-файл

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

Однако, к счастью для нас, FastUtils включает скрипт find-deps.sh , который позволяет генерировать меньшие, более целенаправленные jar-файлы, содержащие только те классы, которые мы хотим использовать в нашем приложении.

4. Коллекции для конкретных типов

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

Для этой цели FastUtils предоставляет интерфейс Double2DoubleMap и реализацию Double2DoubleOpenHashMap :

Double2DoubleMap d2dMap = new Double2DoubleOpenHashMap();

Теперь, когда мы создали экземпляр нашего класса, мы можем просто заполнить данные, как и любую карту из API коллекций Java:

d2dMap.put(2.0, 5.5);
d2dMap.put(3.0, 6.6);

Наконец, мы можем проверить правильность добавления данных:

assertEquals(5.5, d2dMap.get(2.0));

4.1. Производительность

FastUtils фокусируется на своих высокопроизводительных реализациях. В этом разделе мы воспользуемся JMH, чтобы проверить этот факт. Давайте сравним реализацию Java Collections HashSet<Integer> с IntOpenHashSet FastUtil .

Во-первых, давайте посмотрим, как реализовать IntOpenHashSet:

@Param({"100", "1000", "10000", "100000"})
public int setSize;

@Benchmark
public IntSet givenFastUtilsIntSetWithInitialSizeSet_whenPopulated_checkTimeTaken() {
IntSet intSet = new IntOpenHashSet(setSize);
for(int i = 0; i < setSize; i++) {
intSet.add(i);
}
return intSet;
}

Выше мы просто объявили IntOpenHashSet реализацию интерфейса IntSet . Мы также объявили начальный размер setSize с аннотацией @Param .

Проще говоря, эти числа передаются в JMH для проведения серии эталонных тестов с различными размерами наборов.

Далее, давайте сделаем то же самое, используя реализацию коллекций Java:

@Benchmark
public Set<Integer> givenCollectionsHashSetWithInitialSizeSet_whenPopulated_checkTimeTaken() {
Set<Integer> intSet = new HashSet<>(setSize);
for(int i = 0; i < setSize; i++) {
intSet.add(i);
}
return intSet;
}

Наконец, давайте запустим тест и сравним две реализации:

Benchmark                                     (setSize)  Mode  Cnt     Score   Units
givenCollectionsHashSetWithInitialSizeSet... 100 avgt 2 1.460 us/op
givenCollectionsHashSetWithInitialSizeSet... 1000 avgt 2 12.740 us/op
givenCollectionsHashSetWithInitialSizeSet... 10000 avgt 2 109.803 us/op
givenCollectionsHashSetWithInitialSizeSet... 100000 avgt 2 1870.696 us/op
givenFastUtilsIntSetWithInitialSizeSet... 100 avgt 2 0.369 us/op
givenFastUtilsIntSetWithInitialSizeSet... 1000 avgt 2 2.351 us/op
givenFastUtilsIntSetWithInitialSizeSet... 10000 avgt 2 37.789 us/op
givenFastUtilsIntSetWithInitialSizeSet... 100000 avgt 2 896.467 us/op

Эти результаты показывают, что реализация FastUtils намного более производительна, чем альтернатива Java Collections. ``

5. Большие коллекции

Еще одной важной особенностью Fa stUtils является возможность использования 64-битных массивов. Массивы в Java по умолчанию ограничены 32 битами.

Для начала давайте взглянем на класс BigArrays для целочисленных типов. IntBigArrays предоставляет статические методы для работы с двумерными целочисленными массивами. Используя эти предоставленные методы, мы можем по существу обернуть наш массив в более удобный одномерный массив.

Давайте посмотрим, как это работает.

Во-первых, мы начнем с инициализации одномерного массива и преобразования его в двумерный массив с помощью метода переноса IntBigArray :

int[] oneDArray = new int[] { 2, 1, 5, 2, 1, 7 };
int[][] twoDArray = IntBigArrays.wrap(oneDArray.clone());

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

Теперь, как и в случае со списком или картой , мы можем получить доступ к элементам с помощью метода get :

int firstIndex = IntBigArrays.get(twoDArray, 0);
int lastIndex = IntBigArrays.get(twoDArray, IntBigArrays.length(twoDArray)-1);

Наконец, давайте добавим несколько проверок, чтобы убедиться, что наш IntBigArray возвращает правильные значения:

assertEquals(2, firstIndex);
assertEquals(7, lastIndex);

6. Заключение

В этой статье мы подробно рассмотрели основные функции FastUtils . ``

Мы рассмотрели некоторые из коллекций для конкретных типов, которые предлагает FastUtil , прежде чем поиграть с некоторыми BigCollections .

Как всегда, код можно найти на GitHub.