【问题标题】:How can HashMap.keySet() return a view of keys?HashMap.keySet() 如何返回键的视图?
【发布时间】:2018-12-28 08:31:42
【问题描述】:

这是 java.util.HasMap 类中的 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]

【问题讨论】:

  • 顺便说一句,你的HashMapDemo 使用raw types,你应该避免。在此处使用HashMap&lt;Integer, String&gt;Set&lt;Integer&gt;

标签: java memory hashmap inner-classes keyset


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

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

  2. 因为KeySetinner class,它可以访问其包含类(HashMap) 的实例的字段。请注意代码 sn-p 中的 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() 实现,它提供了一个遍历映射键的迭代器,而不是它的值。

【讨论】:

  • 感谢您的回答。我仍然不明白“它提供了 Map 键作为集合的“视图””的含义。您的意思是这里提到的“Set”将在包含 HashMap 键作为其元素的内存上创建吗?如果不是,它是从哪里来的?
  • @Bonsaisteak 的关键元素只存储在HashMap 中。 KeySet 不存储任何内容,它拥有的唯一“字段”是对包含 Map 的隐式引用。 KeySet 的所有方法都使用这个隐式引用来访问甚至修改 Map 的状态。
  • @Bonsaisteak 是的,Set 反映了Map 的内容(特别是它的键)。它不会对新对象进行任何复制或构造 - KeySet 只是调用支持 Map。例如,看一下contains() - 它只是在地图上调用containsKey()HashMap.this 可以省略,因为KeySet 没有containsKey() 方法)。
  • @shmosel 了解到 KeySet 包含对包含映射的引用。但是,我仍然不明白指定它与包含地图键而不是其他数据的联系是什么?
  • @Bonsaisteak 您能否澄清您的问题(也许在对原始帖子的编辑中,这将为您提供更多空间)? KeySet 可以访问地图的所有数据,只需作为一个内部类即可。由于它的目的是显示有关地图键的信息,因此这就是该类实际涉及的所有内容。
猜你喜欢
  • 2015-10-26
  • 2011-07-16
  • 2012-06-22
  • 1970-01-01
  • 1970-01-01
  • 2021-05-17
  • 2021-09-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多