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

Карта примитивов в Java

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

1. Обзор

В этом уроке мы узнаем , как построить карту с примитивными ключами и значениями.

Как мы знаем, основные Java Maps не позволяют хранить примитивные ключи или значения . Вот почему мы представим несколько внешних сторонних библиотек, которые обеспечивают реализации примитивных карт.

2. Коллекции затмений

Eclipse Collections — это высокопроизводительная среда сбора данных для Java . Он предоставляет улучшенные реализации, а также некоторые дополнительные структуры данных, включая несколько примитивных коллекций.

2.1. Изменяемые и неизменяемые карты

Давайте создадим пустую карту, где и ключи, и значения являются примитивными целыми числами. Для этого мы будем использовать фабричный класс IntIntMaps :

MutableIntIntMap mutableIntIntMap = IntIntMaps.mutable.empty();

Фабричный класс IntIntMaps — наиболее удобный способ создания карт-примитивов . Это позволяет нам создавать как изменяемые, так и неизменяемые экземпляры желаемого типа карты. В нашем примере мы создали изменяемый экземпляр IntIntMap . Точно так же мы можем создать неизменяемый экземпляр, просто заменив вызов статической фабрики IntIntMaps.mutable на IntIntMaps.immutable :

ImmutableIntIntMap immutableIntIntMap = IntIntMaps.immutable.empty();

Итак, давайте добавим пару ключ-значение в нашу изменяемую карту:

mutableIntIntMap.addToValue(1, 1);

Точно так же мы можем создавать смешанные карты с парами ключ-значение эталонного и примитивного типов. Давайте создадим карту со строковыми ключами и двойными значениями:

MutableObjectDoubleMap dObject = ObjectDoubleMaps.mutable.empty();

Здесь мы использовали класс фабрики ObjectDoubleMaps для создания изменяемого экземпляра для MutableObjectDoubleMap .

Теперь добавим несколько записей:

dObject.addToValue("price", 150.5);
dObject.addToValue("quality", 4.4);
dObject.addToValue("stability", 0.8);

2.2. Примитивное дерево API

В Eclipse Collections есть базовый интерфейс PrimitiveIterable. Это базовый интерфейс для каждого из примитивных контейнеров библиотеки. Все они называются PrimitiveTypeIterable , где PrimitiveType может быть Int, Long , Short , Byte , Char , Float , Double или Boolean .

Все эти базовые интерфейсы, в свою очередь, имеют свое дерево реализаций XY Map , которое делится на то, является ли карта mutable или immutable . Например, для IntIntMap у нас есть MutableIntIntMap и ImmutableIntIntMap .

Наконец, как мы видели выше, у нас есть интерфейсы, охватывающие все виды комбинаций типов для ключей и значений как для примитивных, так и для объектных значений . Так, например, у нас может быть IntObjectMap<K> для примитивного ключа со значением Object или ObjectIntMap<K> для противоположного случая.

3. HPPC

HPPC — это библиотека, ориентированная на высокую производительность и эффективность использования памяти. Это означает, что в библиотеке меньше абстракций, чем в других. Тем не менее, это имеет преимущество, заключающееся в том, что внутренние компоненты подвергаются полезным низкоуровневым манипуляциям. Он предоставляет как карты, так и наборы.

3.1. Простой пример

Начнем с создания карты с ключом int и значением long . Использование этого довольно знакомо:

IntLongHashMap intLongHashMap = new IntLongHashMap();
intLongHashMap.put(25, 1L);
intLongHashMap.put(150, Long.MAX_VALUE);
intLongHashMap.put(1, 0L);

intLongHashMap.get(150);

HPPC предоставляет карты для всех комбинаций ключей и значений:

  • Примитивный ключ и примитивное значение
  • Примитивный ключ и значение типа объекта
  • Ключ объектного типа и примитивное значение
  • Ключ и значение типа объекта

Карты объектного типа поддерживают дженерики:

IntObjectOpenHashMap<BigDecimal>
ObjectIntOpenHashMap<LocalDate>

Первая карта имеет примитивный ключ int и значение BigDecimal . Вторая карта имеет LocalDate для своих ключей и int для своих значений .

3.2. Хэш-карты против точечных карт

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

Однако место для карт, имеющих более простую функцию распределения, все же остается. Это полезно , если карты используются в качестве таблиц поиска или для подсчета, или если они не требуют большого количества операций записи после загрузки . HHPC предоставляет точечные карты для еще большего повышения производительности.

Все классы точечных карт поддерживают то же соглашение об именах, что и карты, но вместо этого используют слово Scatter :

  • Интскаттерсет
  • IntIntScatterMap
  • IntObjectScatterMap<BigDecimal>

4. Фасттил

Fastutil — это быстрая и компактная платформа , предоставляющая коллекции для конкретных типов, включая карты примитивных типов.

4.1. Быстрый пример

Аналогично Eclipse Collections и HPPC. Fastutil также предоставляет ассоциативные карты типа «примитив-примитив» и «примитив-объект».

Давайте создадим карту int to boolean :

Int2BooleanMap int2BooleanMap = new Int2BooleanOpenHashMap();

А теперь добавим несколько записей:

int2BooleanMap.put(1, true);
int2BooleanMap.put(7, false);
int2BooleanMap.put(4, true);

Затем мы можем получить из него значения:

boolean value = int2BooleanMap.get(1);

4.2. Итерация на месте

Стандартные коллекции JVM, реализующие интерфейс Iterable , обычно создают новый временный объект итератора на каждом шаге итерации. С огромными коллекциями это может создать проблему со сборкой мусора.

Fastutil предоставляет альтернативу, которая значительно смягчает это:

Int2FloatMap map = new Int2FloatMap();
//Add keys here
for(Int2FloatMap.Entry e : Fastutil.fastIterable(map)) {
//e will be reused on each iteration, so it will be only one object
}

Fastutil также предоставляет метод fastForeach . Это возьмет функциональный интерфейс Consumer и выполнит лямбда-выражение для каждого цикла:

Int2FloatMap map = new Int2FloatMap();
//Add keys here
Int2FloatMaps.fastForEach(map , e -> {
// e is also reused across iterations
});

Это очень похоже на стандартную конструкцию foreach в Java :

Int2FloatMap map = new Int2FloatMap();
//Add keys here
map.forEach((key,value) -> {
// use each key/value entry
});

5. Вывод

В этой статье мы узнали, как создавать карты-примитивы на Java с помощью Eclipse Collections, HPPC и Fastutil .

Как всегда, пример кода для этой статьи доступен на GitHub .