Collections.emptyMap()与new HashMap()有何不同?

163
在哪些情况下可以使用 Collections.emptyMap() 呢?文档中提到,如果我想让我的集合不可变,就可以使用这个方法。
为什么我要使用一个不可变的空集合呢?有什么意义呢?

14
因为我之前从未见过这个静态方法,所以我给这个问题点了个+1。 - Tim Bender
你也可以看一下Scala Lenses 谷歌Scala Lenses。emptyMap和immutableMap可用于创建不可变对象。emptyMap是初始点。每次添加元素时,地图本身被包含旧元素和新元素的地图所替换。重点是,对对象的访问是安全的。 - michael_s
1
就像 Objective-C 的 [NSArray array],它返回一个虽然无法使用但存在的对象。因此,您可以像普通对象一样操作它,而不会出现错误。 - Shane Hsu
为了避免空映射获取元素似乎是一个合理的用例。由于它是一个映射,它有方法。返回null并不总是可取的。 - Scratte
8个回答

35

从我的个人经验来看,当API需要一组参数但你没有提供任何内容时,这种方法确实非常有用。例如,你可能有一个类似于下面的API,不允许使用空引用:

public ResultSet executeQuery(String query, Map<String, Object> queryParameters);
如果您有一个不需要任何参数的查询,那么创建一个HashMap肯定有点浪费,因为这涉及分配一个数组,而您可以只传递“空映射”,它实际上是一个常量,以 java.util.Collections 中实现的方式。

25

为什么我要使用不可变的空集合?有什么意义呢?

这里涉及到两个不同的概念,当单独看待这两个概念时会更加合理。

  • 首先,在可能的情况下,您应该优先使用不可变集合而不是可变集合。不可变性的好处在其他地方已经有详细说明

  • 其次,您应该优先使用空集合而不是使用null作为标记。这在此处有很好的描述。这意味着您将拥有更加清晰、易于理解的代码,并且隐藏bug的地方更少。

因此,当您编写需要map的代码时,最好传递一个空map而不是null来表示没有map。并且大多数情况下,使用不可变map比较好。这就是为什么有一个方便的函数可以创建一个不可变的空map。


8

有几种情况下,你更倾向于使用不可变的映射、列表、集合或其他类型的集合。

第一个,也是最重要的用例,是每当你返回查询或计算结果的集合(或列表或映射)时,你应该优先使用不可变数据结构。

在这种情况下,我更喜欢返回这些的不可变版本,因为这更清楚地反映了计算结果集的事实上的不可变性 - 无论你以后对数据做什么操作,你从查询中接收到的结果集都不应该改变。

第二个常见的用例是当你需要将参数作为方法或服务的输入提供。除非你期望输入集合被服务或方法修改(通常是一个非常糟糕的设计想法),在许多情况下,传递一个不可变集合而不是可变集合可能是合理和安全的选择。

我认为这是一种"按值传递"的惯例。

更普遍地说 - 当数据跨越模块或服务边界时,使用不可变数据结构是一种明智的做法。这使得可以更轻松地推理出(不可变的)输入/输出和可变的内部状态之间的差异。

作为一个非常有益的副作用,这增加了模块/服务的安全性和线程安全性,并确保更清晰地分离关注点。

另一个很好的理由使用Collections.empty*()方法是它们明显缺乏冗长。在Java7之前的时代,如果你有一个通用集合,你必须在所有地方洒上通用类型注释。

只需比较以下这两个声明:

Map<Foo, Comparable<? extends Bar>> fooBarMap = new HashMap<Foo, Comparable<? extends Bar>>();

对比:

Map<Foo, Comparable<? extends Bar>> fooBarMap = Collections.emptyMap();

后者在可读性方面明显更胜一筹,有两个重要的原因:
  1. 在第一个声明中,整个空映射的实例化被埋在了泛型类型声明的噪音中,使得本质上微不足道的声明比它需要的更加晦涩难懂。
  2. 除了右侧明显缺乏泛型类型注释之外,第二个版本还清楚地说明了该映射被初始化为空映射。此外——知道这个方法返回一个不可变映射,现在更容易通过搜索“/fooBarMap =/”找到哪里给fooBarMap分配了另一个非空值。

5
首先,您可以使用引用共享。例如 new HashMap() 需要一个分配的对象,可能还需要一些额外的元素来存储数据,但是您只需要一个不可变空集合(列表、集合、映射或任何其他类似的集合)。当您调用的方法需要接受 Map 但不需要编辑它时,这使得它成为一个明显的选择。
我建议查看乔什·布洛克(Josh Bloch)的《Effective Java》,其中列出了一些非常好的不可变对象属性(包括线程安全性)。

3

当你有一个返回不可变集合的函数,但某些情况下没有数据可返回时,你可以返回emptyMap()而不是返回null,这样做可以使你的代码更加简洁,并且可以防止NullPointerException的出现。


3
大多数情况下,我们使用构造函数来创建一个新的空映射。但是,Collections 方法提供了一些优点,可以使用静态 方法java.util.Collections.emptyMap()创建一个空映射
  1. 它们更加简洁,因为您不需要显式地输入集合的泛型类型 - 它通常是从方法调用的上下文中推断出来的。

  2. 它们更加高效,因为它们不会费力地创建新对象;它们只是重复使用现有的空且不可变的对象。这种效果通常非常微小,但偶尔(很少)是重要的。


2
为什么我会想要一个不可变的空集合?这有何意义?
你可能会出于与使用Collections.unmodifiableMap()相同的原因而使用它。你希望返回一个Map实例,如果用户试图修改它,则抛出异常。这只是一个特殊情况:空的Map。

1

为什么我想要一个不可变的空集合?有何意义?

出于与想要不可变对象相同的原因。主要是因为您可以放心地睡觉,因为多个线程可以访问同一对象实例,并且它们都将看到相同的值。在集合中没有项目仍然是有效的值,您需要保持这种状态。


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