使用Map.ofEntries()而不是Map.of()有什么作用?

42
Map.java的文档中得知:

Map.of()Map.ofEntries() 静态工厂方法提供了一种方便的创建不可变映射的方式。

但是,当我已经可以使用重载方法时……

Map.of("k1","v1","k2","v2","k3","v3"...);

Map.ofEntries是什么用途?它返回一个不可变的映射,其中包含从给定条目中提取的键和值,这些条目本身未存储在映射中。

3个回答

70

你知道如何创建一个有26个元素的Map吗?

你已经链接了Map中的两种工厂方法,它们之间的主要区别是:

Map.ofEntries

返回一个不可变的 Map,其中包含从给定的 entries 中提取的键和值(数量未限定)。
来自 JEP-269:集合方便工厂方法

For larger numbers of entries, an API will be provided that will create a Map instance given an arbitrary number of key-value pairs:

Map.ofEntries(Map.Entry<K,V>...)

While this approach is analogous to the equivalent varargs APIs for List and Set, it unfortunately requires that each key-value pair be boxed. A method for boxing keys and values, suitable for static import, will make this more convenient:

Map.Entry<K,V> entry(K k, V v)

关于Map中方法.of()的假设可能有些不正确,这可能是因为在Java9中,尽管这样编译:

List<Integer> values = List.of(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); // note 11 elements here

Set<String> keys = Set.of("z", "o", "tw", "th", "fo", "fi", "si", "se", "e", "n", "te");

然而,这则不会:

Map<String, Integer> map = Map.of("z", 0, "o", 1,
      "tw", 2, "th", 3, "fo", 4, "fi", 5,
      "si", 6, "se", 7, "e", 8, "n", 9, "te", 10); // this would not compile

原因是因为对于 List.ofSet.of 存在 varargs 实现,但是为了创建一个类似的 API 用于 Map,键和值都需要像 JEP 中所述一样被装箱。因此,使用类型为 Map.entry() 的 varargs 创建了相同的实现,如下所示:

Map<String, Integer> map = Map.ofEntries(Map.entry("z",0),
       Map.entry("o",1),Map.entry("t",2)...so on);

此外,从文档中可以看到Map.entry()的介绍,它也是在Since:9引入的 -
返回一个包含给定键和值的不可变的Map.Entry。这些条目适用于使用Map.ofEntries()方法填充Map实例。
通过此方法创建的Entry实例具有以下特点:
- 它们不允许空键或值。尝试使用null键或值创建它们会导致NullPointerException。 - 它们是不可变的。对返回的Entry调用Entry.setValue()会导致UnsupportedOperationException。 - 它们不可序列化。 - 它们是基于值的。调用者不应该对返回的实例的标识做出任何假设。此方法可以自由地创建新实例或重用现有实例。因此,对这些实例进行标识敏感操作(引用相等性(==),标识哈希码和同步)是不可靠的,应该避免使用。
这与最近引入的不可变Map静态工厂方法的特征类似。

14

Java 9推出了一种简洁的单行代码来创建小型不可修改的集合实例,对于映射,工厂方法的签名如下:

Map.of(key1, value1, key2, value2, key3, value3...)

static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3)

该方法被重载以具有0到10个键值对,例如:

Map<String, String> map = Map.of("1", "first");
Map<String, String> map = Map.of("1", "first", "2", "second");
Map<String, String> map = Map.of("1", "first", "2", "second", "3", "third");

同样,您可以拥有最多十个条目。

如果我们有超过10个键值对的情况,有一种不同的方法:

static <K,V> Map<K,V> ofEntries(Map.Entry<? extends K,? extends V>... entries)

以下是使用方法。

Map<String, String> map = Map.ofEntries(
  new AbstractMap.SimpleEntry<>("1", "first"),
  new AbstractMap.SimpleEntry<>("2", "second"),
  new AbstractMap.SimpleEntry<>("3", "third"));

那个链接看起来像是垃圾邮件。我觉得和这里已经回答过的并且在文档中已经链接的内容没有太大区别。 - Naman
3
关于最后一部分的额外说明:我建议使用Map.entry(k, v)而不是 new AbstractMap.SimpleEntry<>(k, v),尽管(自JDK 9以来也可用)。更为简洁。 - Priidu Neemre

13
很简单,Map.of() 不是一个可变参数方法。只有10个以下的重载 Map.of() 方法。另一方面,Map.ofEntries() 是一个可变参数方法,因此允许您指定任意数量的条目。
它们本可以只添加 Map.ofEntries(),但由于很多情况下您只需要几个 条目,他们也包括 Map.of() 版本作为方便之举,这样您就不需要将每个键值对包装在 Entry 中。

2
为什么Map.of()没有可变参数版本? - user5047085
7
关键字和值可以是不同的类型。在Java中没有办法强制表示可变参数中奇数元素为K类型,偶数元素为V类型。ofEntries方法解决了这个问题,因为所有元素都是Entry<K, V>类型(或者更准确地说,是Entry<? extends K, ? extends V>类型,以允许Entry<K, V>的子类型)。 - M. Justin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接