HashMap.keySet() 如何返回密钥视图?



这是java.util.HasMap class中的keySet()函数:

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

在评论中,它说这个功能

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

因此,我期望此函数返回的 KeySet 类型的对象将包含对"键视图"的引用。 但是,当我查看代码时,KeySet 类根本不包含任何字段,也不包含其所有超类。

final class KeySet extends AbstractSet<K> {
public final int size()                 { return size; }
public final void clear()               { HashMap.this.clear(); }
public final Iterator<K> iterator()     { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}

有人可以解释一下

  1. "按键视图"是什么意思?什么样的数据是"视图"?
  2. HashMap.keySet() 如何使用 KeySet 对象返回键的视图,该对象根本不包含对任何内容的引用?

为了更清楚:

下面是打印出密钥集值的代码示例。尽管 KeySet 对象包含对包含映射数据的引用,但这如何准确地输出键数据而不是映射的其他数据(不是值数据或其他任何内容)。是什么告诉这个KeySet对象只保存MapKeys?我在其代码中看不到这样的指令。

package com.tutorialspoint;
import java.util.*;
public class HashMapDemo {
public static void main(String args[]) {
// create hash map
HashMap newmap = new HashMap();
// populate hash map
newmap.put(1, "tutorials");
newmap.put(2, "point");
newmap.put(3, "is best");
// get keyset value from map
Set keyset = newmap.keySet();
// check key set values
System.out.println("Key set values are: " + keyset);
}    
}

输出 :

关键集值为:[1, 2, 3]

  1. "视图"是一种对象,其数据由不同的对象支持,但以不同的方式提供。在这种情况下,它提供了Map键的"视图"作为Set。这对用户和性能都有很多好处。

    值得注意的是,由于它与后备类共享其数据,因此内存开销很少 - 它不需要将所有密钥复制到一个全新的Set中。此外,用户无需担心视图与支持结构不同步 - 添加和删除将立即通过视图可见。

  2. 因为KeySet是一个内部类,所以它可以访问其包含类(HashMap)的实例的字段。请注意代码段中的HashMap.this表示法。

    • size()return size;是对HashMapsize域的引用
    • clear()HashMap.this.clear();调用HashMapclear()方法(它需要使用HashMap.this来引用映射的clear()方法而不是它自己的方法)
    • contains()代表HashMapcontainsKey()方法 - 这不需要HashMap.this,因为KeySet没有冲突containsKey()方法
    • remove()同样被委派给HashMapremoveNode()

    如果它被声明为final static class KeySet它将是一个静态嵌套类,它不绑定到包含类的实例(它只是组织相关类的一种方式)。在这种情况下,KeySet需要一个显式HashMap字段,并且需要将有问题的映射传递到构造函数中。内部类使这成为隐式的(这是简洁的,但有时肯定会令人困惑)。

  3. 是什么告诉这个KeySet对象只保存MapKeys?

    需要明确的是,没有这样的指示。KeySet实例可传递地访问后备映射的所有字段和方法。但是您的示例(System.out.println("Key set values are: " + keyset);)隐式调用keyset.toString(),它(故意)没有实现以返回映射的值。因为KeySet扩展AbstractSet(进而扩展AbstractCollection),它的.toString()依赖于KeySetiterator()实现,它提供了一个迭代器来分析映射的键,而不是它的值。

最新更新