java - How can HashMap.keySet() return a view of keys?
问题描述
Here is the keySet() function inside java.util.HasMap class :
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
In the comment, it says this function
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.
Therefore, I expected the object of KeySet type, which this functions returns would contain a reference to the "view of keys". However when I look at the code, the KeySet class does not contain any field at all, and all of its super classes either.
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();
}
}
}
Could somebody please explain
- What does "a view of the keys" mean? What kind of data is "a view"?
- How can the HashMap.keySet() returns a view of the keys by using KeySet object, which doesn't contain any reference to anything at all?
To make it more clear :
Here is a code sample to print out the keyset value. Although the KeySet object holds the reference to the containing map data, how can this exactly output the key data but not other data of the map (not value data or anything else). What is the thing that tells this KeySet object to hold only MapKeys? I can't see such instruction in its code.
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);
}
}
Ouput :
Key set values are: [1, 2, 3]
解决方案
A "view" is a type of object whose data is backed by a different object, but is provided in a different way. In this case, it's providing a "view" of the
Map
's keys as aSet
. This has a number of benefits, both for users and for performance.Notably, since it shares its data with the backing class there's very little memory overhead - it doesn't need to copy all the keys into a whole new
Set
. Additionally, users don't need to worry about the view getting out of sync with the backing structure - additions and removals will be visible via the view immediately.Because
KeySet
is an inner class it has access to the fields of an instance of its containing class (HashMap
). Notice theHashMap.this
notation in your code snippet.size()
'sreturn size;
is a reference toHashMap
'ssize
fieldclear()
'sHashMap.this.clear();
callsHashMap
'sclear()
method (it needs to useHashMap.this
to reference the map'sclear()
method rather than its own)contains()
delegatesHashMap
'scontainsKey()
method - this doesn't requireHashMap.this
becauseKeySet
doesn't have a collidingcontainsKey()
methodremove()
similarly delegates toHashMap
'sremoveNode()
If it was instead declared as
final static class KeySet
it would be a static nested class, which is not tied to an instance of the containing class (it's just a way of organizing related classes). In that caseKeySet
would need an explicitHashMap
field, and the map in question would need to be passed into the constructor. Inner classes make this implicit (which is concise, but certainly sometimes confusing).What is the thing that tells this KeySet object to hold only MapKeys?
To be clear, there is no such instruction. A
KeySet
instance transitively has access to all the fields and methods of the backing map. However your example (System.out.println("Key set values are: " + keyset);
) is implicitly callingkeyset.toString()
, which is (intentionally) not implemented to return the map's values. BecauseKeySet
extendsAbstractSet
(which in turn extendsAbstractCollection
) its.toString()
relies on theKeySet
'siterator()
implementation, which provides an iterator over the map's keys, not its values.
推荐阅读
- javascript - 如何在后台同时运行多个 Grunt 脚本?
- ms-access - 为什么此查询更改有效?
- sql-server - SQL - 为采购数量的 DESC 顺序中的每一行分配编号
- apache-spark - 如何在处理不平衡的执行器工作负载时重新平衡 RDD
- python - DRF 将数据传递到相关字段
- hibernate - Travis-CI 中的集成错误
- xml - 在图像android上绘制一个矩形
- visual-studio - Visual Studio SDK - 获取当前行分隔符
- php - 如何通过 http Web 界面在 ssh 上运行 rsync 以启动 rsync 以将文件从 server2 移动到 server1
- c# - 如果值为字符串类型,则按片段搜索,但如果值为 int,则按整个搜索