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

时间:2018-07-20 05:25:29

标签: java memory hashmap inner-classes keyset

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

@JvmStatic

在评论中,它说明了此功能

  

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

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

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

有人可以解释

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

为了更加清楚:

这是一个代码示例,用于打印出键集值。尽管KeySet对象保留了对包含的地图数据的引用,但是如何才能准确地输出键数据而不是地图的其他数据(不是值数据或其他任何数据)。告诉此KeySet对象仅保留MapKeys的东西是什么?我在其代码中看不到这样的指令。

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、3]

1 个答案:

答案 0 :(得分:8)

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

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

  2. 由于KeySetinner class,因此可以访问其包含类(HashMap)的实例的字段。请注意代码段中的HashMap.this表示法。

    • size()的{​​{1}}是对return size;的{​​{1}}字段的引用
    • HashMap的{​​{1}}调用size的{​​{1}}方法(它需要使用clear()来引用地图的HashMap.this.clear();方法比它自己的)
    • HashMap代表clear()的{​​{1}}方法-这不需要HashMap.this,因为clear()没有冲突的contains()方法
    • HashMap类似地委托给containsKey()的{​​{1}}

    如果改为声明为HashMap.this,则它将是一个静态嵌套类,它不与包含类的实例绑定(这只是组织相关类的一种方式)。在那种情况下,KeySet将需要一个显式的containsKey()字段,并且所涉及的映射将需要传递到构造函数中。内部类使此隐式(简明扼要,但有时有时会令人困惑)。

  3.   

    告诉此KeySet对象仅保留MapKeys是什么?

    为清楚起见,没有此类说明。 remove()实例可传递地访问支持映射的所有字段和方法。但是,您的示例(HashMap)隐式调用removeNode(),(有意地)未实现该操作以返回映射的值。由于final static class KeySet扩展了KeySet(又扩展了HashMap),因此其.toString()依赖于KeySet的{​​{1}}实现,该实现提供了迭代器而不是地图的值。