【问题标题】:Collections.emptyMap() vs new HashMap()Collections.emptyMap() 与新的 HashMap()
【发布时间】:2013-01-28 14:35:00
【问题描述】:

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

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

【问题讨论】:

  • +1 这个问题,因为我以前从未见过这种静态方法
  • 你也可以看看 scala 镜头google scala lenses。 emptyMap 和 immutableMap 可用于创建不可变的对象。 emptyMap 是初始点。每次添加元素时,地图本身都会被包含旧元素和新元素的地图替换。关键是,对对象的访问是安全的。
  • 就像Objective-C的[NSArray array]一样,它返回一个虽然不可用但存在的对象。因此,您可以像使用普通对象一样使用它而不会出错。
  • @Sнаđошƒаӽ 避免空地图获取元素似乎是一个合理的用例。因为它是一张地图,所以它有方法。返回 null 并不总是可取的。

标签: java collections


【解决方案1】:

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

这里有两个不同的概念,放在一起看时会显得很奇怪。分开处理这两个概念会更有意义。

  • 首先,您应该尽可能使用不可变集合而不是可变集合。不可变的好处很好documented elsewhere。

  • 其次,您应该更喜欢使用空集合而不是使用 null 作为标记。这是well described here。这意味着您将拥有更清晰、更易于理解的代码,隐藏错误的地方更少。

因此,当您有需要映射的代码时,最好传递一个空映射而不是 null 来指示不存在映射。而且大多数情况下,当您使用地图时,最好使用不可变地图。所以这就是为什么有一个方便的函数来制作一个不可变的空地图。

【讨论】:

    【解决方案2】:

    来自 Effective Java,Item #43 - "Return empty arrays or collections, not null" 演示返回一个空集合,甚至可能演示使用这些 emptyList()、emptySet() 和 @987654325 @ Collections 类上的方法来获取一个空集合,该集合还具有不可变的额外好处。来自Item #15 "Minimize Mutability"。

    来自Collections-emptySet-Collections-emptyList-Collections

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

    注意:下面的代码只是一个例子(根据你的用例改变它):

    private Set myset = Collections.emptySet();
    
    void initSet() {
       myset = new HashSet();
    }
    void deleteSet() {
       myset = Collections.emptySet();
    }
    

    这些方法有几个优点:

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

    2. 它们更高效,因为它们不用费心创建新对象;他们只是重新使用现有的空且不可变的对象。这种影响通常非常轻微,但偶尔(嗯,很少)很重要。

    【讨论】:

    • +1 用于有效 Java 参考。一点点:我建议在您的示例中参数化Set 和HashSet,因为emptySet() 方法和朋友(与常量Collections.EMPTY_SET 等相反)的全部意义在于它们与泛型很好地配合。另外,使用自 Java 5 以来已被弃用的特性(原始类型)并不是一个好的教学辅助工具。
    • 我仍然不相信...使用Collection 而不是null 来避免Exceptions 在后续操作中被抛出,难道不是重点吗?使用不可变集合只会导致我想象的其他某种异常。并且分配 null 肯定不会比分配不可变常量的效率低。
    • @fgysin:如果您有一个 API 要求客户端修改集合,那么是的,返回不可变集合是没有意义的。但是,如果您有一个 API,您返回一个客户端不得修改并且应该只对其进行迭代的集合,那么将视图返回到底层集合是非常有意义的,以确保“坏”客户端不会意外修改拥有的集合由你。返回一个空集合而不是 null 意味着您的客户端在使用该集合之前不必进行 null 检查,从而使客户端代码更清晰。
    • public boolean setExists() { return !myset.equals(Collections.emptySet()); }
    • 在您的示例中,您正在显式检查空集并将其用作标记值,并且您的类是可变的。你的小例子使用了哨兵和可变性,这正是 Collections.emptySet() 试图阻止的。
    【解决方案3】:

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

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

    在这种情况下,我更喜欢返回这些的不可变版本,因为这更清楚地反映了计算结果集的事实不变性 - 无论您以后如何处理数据,您从您的查询不应更改。

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

    我认为它是 “按值传递” 约定。

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

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

    另一个很好的理由使用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 被分配另一个 nonempty 值的位置,只需搜索 /fooBarMap =/。

    【讨论】:

      【解决方案4】:

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

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

      【讨论】:

        【解决方案5】:

        大多数时候我们使用constructor 来创建一个新的empty map。但是Collectionsmethods 提供了一些优势来创建一个empty map 使用staticmethodjava.util.Collections.emptyMap()

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

        2. 它们更高效,因为它们不用费心创建新对象;他们只是重新使用现有的空且不可变的对象。这 影响通常很小,但偶尔会(嗯,很少) 很重要。

        【讨论】:

          【解决方案6】:

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

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

          【讨论】:

            【解决方案7】:

            首先,您可以摆脱参考共享。 new HashMap() 等将需要一个分配的对象,可能还需要一些额外的元素来保存数据,但您只需要一个不可变的空集合(列表、集合、映射或任何其他此类)的副本。当您调用的方法需要接受 Map 但不需要对其进行编辑时,这使其成为一个明显的选择。

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

            【讨论】:

              【解决方案8】:

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

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

              【讨论】:

                【解决方案9】:

                诚然,就我个人的经验而言,它在 API 需要一组参数但您没有提供任何东西的情况下非常有用。例如,您可能有一个看起来像这样的 API,并且不允许空引用:

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

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

                【讨论】:

                  猜你喜欢
                  • 2012-10-20
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-03-31
                  • 2014-10-23
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-03-16
                  • 2011-04-09
                  相关资源
                  最近更新 更多