Java原始类型Map

2023/06/07

1. 概述

在本教程中,我们将学习如何使用原始类型键和值构建Map

正如我们所知,核心Java Map不允许存储原始类型键或值。这就是为什么我们会引入一些提供原始类型Map实现的外部第三方库。

2. Eclipse Collections

Eclipse Collections是Java的高性能集合框架。它提供了改进的实现以及一些额外的数据结构,包括几个原始集合。

2.1 可变和不可变Map

让我们创建一个空Map,其中键和值都是原始int。为此,我们将使用IntIntMaps工厂类:

MutableIntIntMap mutableIntIntMap = IntIntMaps.mutable.empty();

IntIntMaps工厂类是创建原始Map最方便的方法。它允许我们创建所需Map类型的可变和不可变实例。在我们的示例中,我们创建了IntIntMap的可变实例。类似地,我们可以通过简单地将IntIntMaps.mutable静态工厂调用替换为IntIntMaps.immutable来创建不可变实例:

ImmutableIntIntMap immutableIntIntMap = IntIntMaps.immutable.empty();

因此,让我们向可变Map中添加一个键值对:

mutableIntIntMap.addToValue(1, 1);

同样,我们可以创建具有引用和原始类型键值对的混合Map。让我们创建一个包含String键和double值的Map:

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。

反过来,所有这些基础接口都有它们的XYMap实现树,它根据Map是可变的还是不可变的来划分。例如,对于IntIntMap,我们有MutableIntIntMap和ImmutableIntIntMap。

最后,正如我们在上面看到的,我们有接口来涵盖原始类型值和对象值的键和值的所有类型组合。因此,例如,我们可以将IntObjectMap<K>用于具有Object值的原始类型键,或者将ObjectIntMap<K>用于相反的情况。

3. HPPC

HPPC是一个面向高性能和内存效率的库。这意味着该库的抽象程度低于其他库。但是,这样做的好处是可以将内部结构暴露给有用的低级操作。它同时提供Map和集合。

3.1 一个简单的例子

让我们首先创建一个具有int键和long值的Map,使用这个是非常熟悉的:

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

HPPC为键和值的所有组合提供Map:

  • 原始类型键和原始类型值
  • 原始类型键和对象类型值
  • 对象类型键和原始类型值
  • 对象类型键和对象类型值

对象类型Map支持泛型:

IntObjectOpenHashMap<BigDecimal>
ObjectIntOpenHashMap<LocalDate>

第一个Map具有原始int键和BigDecimal值,第二个Map的键是LocalDate,值是int

3.2 HashMap与ScatterMap

由于键哈希和分布函数的传统实现方式,我们在哈希键时可能会发生冲突。根据键的分布方式,这可能会导致大型Map上的性能问题。默认情况下,HPPC实施了避免此问题的解决方案。

但是,具有更简单分布函数的Map仍然有一席之地。如果Map用作查找表或用于计数,或者如果它们在加载后不需要大量写入操作,这将很有用。HPPC提供ScatterMap以进一步提高性能。

所有ScatterMap类都保持与Map相同的命名约定,但改用单词Scatter:

  • IntScatterSet
  • IntIntScatterMap
  • IntObjectScatterMap<BigDecimal>

4. Fastutil

Fastutil是一个快速而紧凑的框架,它提供特定于类型的集合,包括原始类型Map。

4.1 快速示例

类似于Eclipse Collections和HPPC。Fastutil也提供原始类型到原始类型和原始类型到对象类型的关联映射。

让我们创建一个int到boolean Map:

Int2BooleanMap int2BooleanMap = new Int2BooleanOpenHashMap();

现在,让我们添加一些条目:

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

然后,我们可以从中检索值:

boolean value = int2BooleanMap.get(1);

4.2 就地迭代

实现Iterable接口的标准JVM集合通常在每个迭代步骤创建一个新的临时迭代器对象。对于庞大的集合,这可能会产生垃圾回收问题。

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函数接口并为每个循环执行Lambda表达式:

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

这与标准的Java foreach结构非常相似:

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

5. 总结

在本文中,我们学习了如何使用Eclipse Collections、HPPC和Fastutil在Java中创建原始Map。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章