Collections.emptyMap()vs new HashMap()

Vin*_*C M 133 java collections

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

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

Sum*_*ngh 137

从有效的Java, 项目#43 - "Return empty arrays or collections, not null"演示返回一个空的集合,甚至演示如何使用这些emptyList(),emptySet()和emptyMap()对集合类的方法来得到一个空的集合,也有保持不变的额外好处.来自 第15项 "Minimize Mutability".

来自Collections-emptySet-Collections-emptyList-Collections

它是一种编程习语.这适用于不想要空变量的人.因此,在初始化集合之前,他们可以使用空集.

注意:下面的代码只是一个示例(根据您的使用情况进行更改):

private Set myset = Collections.emptySet();

void initSet() {
   myset = new HashSet();
}
void deleteSet() {
   myset = Collections.emptySet();
}
Run Code Online (Sandbox Code Playgroud)

这些方法提供了几个优点:

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

  2. 它们更有效率,因为它们不会打扰创建新对象; 他们只是重用现有的空和不可变对象.这种效果通常很小,但偶尔(很少,很少)很重要.

  • @fgysin:如果你有一个API要求客户端修改集合,那么是的,返回一个不可变的集合是没有意义的.但是如果你有一个API,你返回一个客户端不能修改并且应该迭代的集合,那么将一个视图返回到底层集合是完全合理的,以确保"坏"客户端不会意外地修改所拥有的集合由你.返回空集合而不是null意味着您的客户端在使用集合之前不必进行空检查,从而使客户端代码更加清晰. (14认同)
  • 有效Java参考的+1.一个尼特:我建议在你的例子中参数化`Set`和`HashSet`,因为`emptySet()`方法和朋友(而不是常量`Collections.EMPTY_SET`等)的整点是他们玩的很好地与泛型.此外,使用自Java 5以来已弃用的功能(原始类型)不是一个好的教学辅助工具. (13认同)
  • 在您的示例中,您明确检查空集并将其用作标记值,并且您的类是可变的.你的小例子使用了哨兵和可变性,这正是Collections.emptySet()试图阻止的. (5认同)
  • 我仍然不相信......使用`Collection`而不是`null`的全部观点是不是要避免在操作后抛出"Exceptions"?使用不可变集合只会导致我想象的其他异常.并且分配"null"肯定不比分配不可变常量有效. (4认同)
  • `public boolean setExists(){return!myset.equals(Collections.emptySet()); }` (4认同)
  • Bloch先生没有告诉你'在使用equals/compare时调用常量而不是未知对象的方法'即`Collections.emptySet().equals(myset)`和第二次尝试使用equals()时常用`==`而不是.第一个习惯用法的原因 - >在常量上调用虚方法不需要多态,虚拟表(或内联缓存) (3认同)

Aff*_*ffe 32

诚然,根据我的个人经验,在API需要参数集合的情况下非常有用,但您无需提供任何参数.例如,您可能有一个看起来像这样的API,并且不允许空引用:

public ResultSet executeQuery(String query, Map<String, Object> queryParameters);
Run Code Online (Sandbox Code Playgroud)

如果你有一个不带任何参数的查询,那么创建一个HashMap肯定有点浪费,它涉及分配一个数组,当你可以传入实际上是常量的'Empty Map'时,它的实现方式在java.util.Collections.


Mar*_*ain 21

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

这里有两个不同的概念,一起看时看起来很奇怪.当你分别处理这两个概念时,它会更有意义.

  • 首先,您应该尽可能使用不可变集合而不是可变集合.其他地方都有很好的文献记载.

  • 其次,您应该更喜欢使用空集合而不是使用null作为标记.这里有很好的描述.这意味着您将拥有更清晰,更易于理解的代码,并且可以减少隐藏错误的位置.

因此,当您拥有需要地图的代码时,最好传递一个空地图而不是空来表示没有地图.大多数情况下,当您使用地图时,最好使用不可变地图.所以这就是为什么有一个便利函数来创建一个不可变的空映射.


Rol*_*epp 8

有几种情况您更喜欢使用不可变的地图,列表,集合或其他类型的集合.

首先,可以说是最重要的用例是,无论何时返回查询结果或返回结果集(或列表或映射)的计算,您都应该使用不可变数据结构.

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

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

我认为它是"按价值传递"的惯例.

更一般地说 - 只要数据跨越模块或服务边界,就使用不可变数据结构是一种明智的做法.这使得更容易推理(不可变)输入/输出和可变内部状态之间的差异.

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

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

只需比较这两个声明:

Map<Foo, Comparable<? extends Bar>> fooBarMap = new HashMap<Foo, Comparable<? extends Bar>>();
Run Code Online (Sandbox Code Playgroud)

与:

Map<Foo, Comparable<? extends Bar>> fooBarMap = Collections.emptyMap();
Run Code Online (Sandbox Code Playgroud)

后者显然在两个重要方面取得了可读性:

  1. 在第一个声明中,空映射的整个实例化都隐藏在泛型类型声明的噪声中,使得基本上无关紧要的声明比它需要的更加神秘.
  2. 除了在右侧显着缺少泛型类型注释之外,第二个版本明确指出地图被初始化为空地图.另外 - 知道这个方法返回一个不可变的映射,现在我更容易通过搜索找到fooBarMap另一个非空值的位置/fooBarMap =/.


Jef*_*ica 5

首先,您可以通过参考共享来逃避.A new HashMap()等将需要一个已分配的对象,并且可能需要一些额外的元素来保存数据,但是您只需要一个不可变空集合的副本(列表,集合,映射或任何其他类似).当您调用的方法需要接受Map但不需要编辑它时,这是一个明显的选择.

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