【问题标题】:Why doesn't LinkedHashMap keyset() return LinkedHashSet vs Set?为什么 LinkedHashMap keyset() 不返回 LinkedHashSet vs Set?
【发布时间】:2016-02-08 17:18:54
【问题描述】:

我在 java 文档和这篇文章中读到 LinkedHashMaps 的keyset() 维护秩序。 Is the order guaranteed for the return of keys and values from a LinkedHashMap object?

我的问题是,如果它保证了顺序,那么为什么LinkedHashMap 的源代码不返回一个类型为Set 的对象来保证像LinkedHashSet 这样的顺序?

我可能想到的一个原因是 LinkedHashSet 使用了一个会增加内存分配的映射(取决于 AbstractSet 的实现方式)。是否也是因为它未来证明了密钥集的实现?

就像这个答案在这篇文章中所说的那样:Is it better to use List or Collection?

返回列表符合编程到最合适的 界面。

返回 Collection 会给用户带来歧义,因为 返回的集合可以是:Set、List 或 Queue。

那么,不阅读keyset() 的文档,这不是模棱两可吗?

keyset()源代码:

public Set<K> keySet() {
    Set<K> ks = keySet;
    return (ks != null ? ks : (keySet = new KeySet()));
}

private final class KeySet extends AbstractSet<K> {
    public Iterator<K> iterator() {
        return newKeyIterator();
    }
    public int size() {
        return size;
    }
    public boolean contains(Object o) {
        return containsKey(o);
    }
    public boolean remove(Object o) {
        return HashMap.this.removeEntryForKey(o) != null;
    }
    public void clear() {
        HashMap.this.clear();
    }
}

【问题讨论】:

    标签: java collections linkedhashmap linkedhashset


    【解决方案1】:

    “为什么LinkedHashMap 的源代码不返回一个Set 类型的Object 来保证像LinkedHashSet 这样的顺序?”

    因为LinkedHashSet 是一个具体的类,它有自己的实现维护自己的数据,而keyset() 方法必须返回Map 的视图,所以它不能只将关键数据复制到LinkedHashSet。请参阅 javadoc:

    返回此映射中包含的键的Set 视图。 集合由地图支持,因此对地图的更改会反映在集合中,反之亦然。

    【讨论】:

      【解决方案2】:

      它返回由Map 接口定义的SetSet 只是不包含重复元素的集合。另一方面,List 是有序集合(也称为序列)。 List 接口没有指定元素是唯一的。

      不过,LinkedHashMap 实现实际上返回底层 keySet 是一个 LinkedKeySet,而 LinkedKeySetforEach() 方法是保序的,即:

      //from the source code for the LinkedHashMap:
      public Set<K> keySet() {
          Set<K> ks;
          return (ks = keySet) == null ? (keySet = new LinkedKeySet()) : ks;
      }
      

      因此,在这种情况下,元素是唯一且有序的。

      【讨论】:

      • 虽然您的第二部分是正确的,但第一部分不一定正确,因为 Map 接口指定 Set 并不意味着它不能用 LinkedKeySet 覆盖
      • 我的意思是强调LinkedKeySet 提供了对其元素的排序。我不想暗示Set 也不能实现List 接口。 @Andreas 回答解决了您关于“为什么”的问题;我试图解决我们该怎么做方面。
      【解决方案3】:

      既然保证了顺序,那为什么LinkedHashMap的源代码不返回像LinkedHashSet一样保证顺序的Set类型的Object呢?

      密钥集的声明 类型取自Map.keySet() 声明的类型。在将协变返回类型添加到 Java 语言之前,LinkedHashMap 已添加到标准库中。在那个时候,除了使用Set之外别无他法。

      无论如何,仅仅因为键集必须与底层映射具有相同的顺序并不意味着它必须是LinkedHashSet,事实上它不可能,因为它一定不支持元素添加。它的类是 LinkedHashMap 类的私有嵌套类。在该类和 Set 之间没有任何公共类型比 Set 作为返回类型更有用。

      确实,我认为定义一个并不容易。将它与任何其他集合区分开来的基本特征是什么?您不能只说“迭代顺序”而不指定 什么 迭代顺序实例将具有,并且该顺序取决于从中获取集合的 LinkedHashMap 实例。换句话说,迭代顺序是实例特征,而不是类特征。因此,一个声明的类型表示为 LinkedHashMap 的键集的性质,因此除了 Set 提供的信息之外,几乎不会提供任何实际用途的信息。

      无论如何,我没有发现你引用的关于返回 ListCollection 的摘录非常有说服力。事实上,关于对象的特定类的模糊性本质上不是问题。在CollectionList 的示例中,如果Collection 类型很好地捕获了方法的返回类型要求,因此没有特别的理由需要List 的特征,那么@987654337 @ 是一个不错的返回类型选择。如果Set 也可以满足要求,那就太好了,因为这样 API 就不会在两个同样好的替代方案之间做出本质上任意的选择。

      有一种观点认为,方法的参数类型应尽可能通用,返回类型应尽可能具体。我认为这个想法有很多优点,但它更像是一个指导方针而不是规则,关于“尽可能具体”的具体程度还有很大的解释空间。

      【讨论】:

        猜你喜欢
        • 2013-07-31
        • 1970-01-01
        • 1970-01-01
        • 2015-11-17
        • 2016-10-11
        • 1970-01-01
        • 1970-01-01
        • 2011-01-11
        相关资源
        最近更新 更多