EmptyMap()和 new HashMap()

在哪些情况下我可以使用 Collections.emptyMap()?文档说,如果我希望我的集合是不可变的,我可以使用这个方法。

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

60413 次浏览

诚然,以我个人的经验来看,在 API 需要一组参数的情况下,它是非常有用的,但是您不需要提供任何参数。例如,您可能有一个类似下面这样的 API,它不允许空引用:

public ResultSet executeQuery(String query, Map<String, Object> queryParameters);

如果你有一个不带任何参数的查询,那么创建一个 HashMap 肯定是有点浪费的,因为它涉及到分配一个数组,而你只需要传递一个“空映射”,它实际上是一个常量,就像它在 java.util.Collections中实现的那样。

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

出于同样的原因,你会在某些时候使用 Collections.unmodifiableMap()。您希望返回一个 Map 实例,该实例在用户试图修改它时引发异常。这只是一个特例: 空地图。

首先,你可以不用担心参考共享的问题。new HashMap()等需要一个已分配的对象,可能还需要一些额外的元素来保存数据,但是您只需要一个不可变的空集合(list、 set、 map 或其他类似的集合)的一个副本。当您正在调用的方法需要接受 Map 但不需要编辑它时,这是一个显而易见的选择。

我建议查看 Josh Bloch 的 有效的爪哇,它列出了一些非常好的不可变对象属性(包括线程安全)。

当你有一个返回 immutable collection的函数,并且在某些情况下没有数据可以返回,所以你可以返回 emptyMap()而不是返回 null时,它会很有用

它使您的代码更容易,并防止 NullPointerException

大多数情况下,我们使用 constructor来创建一个新的 empty map。但是 Collections methods提供了一对优势来创建使用 static method java.util.Collections.emptyMap()empty map

  1. 它们更加简洁,因为您不需要显式地键入集合的通用类型-它通常只是从 方法调用的上下文

  2. 它们更有效率,因为它们不需要创建新对象,它们只是重用现有的空对象和不可变物件 影响通常是非常小的,但偶尔(好吧,很少) 很重要

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

出于同样的原因,您可能需要不可变对象。主要是因为您知道多个线程可以访问一个对象的相同实例,并且它们都将看到相同的值,因此可以在晚上安全睡眠。在集合中没有项仍然是一个有效的值,您需要维护这个值。

在有些情况下,您更愿意使用不可变的映射、列表、集合或其他类型的集合。

第一个 可以说是最重要的用例是,当您返回一个查询结果或计算结果集(或列表或映射)时,您应该更喜欢使用不可变的数据结构。

在这种情况下,我更倾向于返回这些结果的不可变版本,因为这更清楚地反映了计算结果集的实际不可变性——无论以后如何处理数据,从查询中获得的结果集都不应该更改。

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

我认为这是 “按值传递”大会。

更一般的 ——当数据跨越模块或服务边界时,使用不可变的数据结构是明智的做法。这使得推断(不可变的)输入/输出和可变的内部状态之间的差异变得更加容易。

作为一个非常有益的副作用,这是提高安全性和线程安全的模块/服务,并确保更清洁的关注点分离。

使用 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被分配到另一个 不是空的值的位置。

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

这里有两个不同的概念放在一起看起来很奇怪。将这两个概念分开处理更有意义。

  • 首先,您应该尽可能使用不可变的集合,而不是可变的集合。免疫的好处是良好的 记录在案

  • 其次,您应该更喜欢使用空集合,而不是使用 null 作为哨兵。这是 这里描述得很好。这意味着您将拥有更清晰、更容易理解的代码,更少的地方可以隐藏 bug。

因此,当您有需要映射的代码时,最好传递一个空映射而不是 null 来表示缺少映射。在大多数情况下,当您使用映射时,最好使用不可变映射。这就是为什么有一个方便的函数来创建一个不可变的空映射。