HashMap.keySet() return 如何查看键值?

How can HashMap.keySet() return a view of keys?

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

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

评论里说的是这个功能

Returns a {@link Set} view of the keys contained in this map. The set is backed by the map, so changes to the map are reflected in the set, and vice-versa.

因此,我期望 KeySet 类型的对象,此函数 returns 将包含对 "view of keys" 的引用。 但是,当我查看代码时,KeySet class 根本不包含任何字段,而且它的所有超级 class 也是如此。

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. "a view of the keys"是什么意思? "a view"是什么数据?
  2. HashMap.keySet() returns 如何使用根本不包含任何引用的 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);
   }    
}

输出:

Key set values are: [1, 2, 3]

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

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

  2. 因为 KeySet 是一个 inner class 它可以访问包含 class (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,它将是一个静态嵌套的 class,它不依赖于包含 class 的实例(这只是一种组织方式相关 classes)。在那种情况下 KeySet 需要一个明确的 HashMap 字段,并且需要将有问题的地图传递给构造函数。内部 classes 使这一点变得隐含(简洁,但有时确实令人困惑)。

  3. What is the thing that tells this KeySet object to hold only MapKeys?

    需要说明的是,没有这样的指令。 KeySet 实例可传递地访问支持映射的所有字段和方法。但是,您的示例 (System.out.println("Key set values are: " + keyset);) 正在隐式调用 keyset.toString(),这(有意)未实现为 return 地图的值。因为 KeySet 扩展了 AbstractSet(进而扩展了 AbstractCollection)它的 .toString() 依赖于 KeySetiterator() 实现,它提供了一个迭代器在地图的键上,而不是它的值上。