深入理解keySet、values、entrySet
HashMap遍历的方式
Map<String,String> map = new HashMap<>();
//1、for循环遍历key
System.out.println("通过Map.keySet遍历key和value:");
for (String key: map.keySet()){
System.out.println("key:"+key+" value:"+map.get(key));
}
//2、for循环遍历value
System.out.println("通过Map.values()遍历所有的value,但ey");
for(Object v:map.values())
{
System.out.println("value:"+v);
}
//3、for循环遍历entrySet
System.out.println("通过Map.entrySet遍历key和value");
for(Map.Entry<String, String> entry: map.entrySet())
{
System.out.println("Key: "+ entry.getKey()+ " Va"+entry.getValue());
}
//4、iterator
System.out.println("通过Map.entrySet使用iterator遍历key和va");
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while(iterator.hasNext()){
Map.Entry<String,String> entry = iterator.next();
System.out.println("key:"+entry.getKey()+" value:"+entry.getValue());
}
在遍历HashMap的时候:
Set<String> setStrings = map.keySet()
for (String key: setStrings){
System.out.println("key:"+key+" value:"+map.get(key));
}
但是keySet()是如何获取到HashMap的所有的key呢?
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
其中keySet、values是AbstractMap中声明的变量:
transient Set<K> keySet;
transient Collection<V> values;
调用map.keySet()时,会判断ks是否为空,然后调用new KeySet(),keySet()方法返回一个引用,这个引用指向了HashMap的一个内部类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();
}
}
}
此内部类继承了AbstractSet,此内部类初始化迭代器产生一个迭代器对象KeyIterator:
final class KeyIterator extends HashIterator
implements Iterator<K> {
public final K next() { return nextNode().key; }
}
在使用增强for循环遍历map.keySet()时,就是调用的KeyIterator.next()方法。
KeyIterator类继承了HashIterator迭代器:
abstract class HashIterator {
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
HashIterator() {
expectedModCount = modCount;
Node<K,V>[] t = table;
current = next = null;
index = 0;
if (t != null && size > 0) { // advance to first entry
do {} while (index < t.length && (next = t[index++]) == null);
}
}
public final boolean hasNext() {
return next != null;
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
HashIterator迭代器初始化拿到了next指向map中的第一个元素。当使用keySet集合遍历key时,其实是使用迭代器KeyIterator迭代每个节点的key。
问题:
在使用增强for循环遍历map.keySet()时,为什么调用的KeyIterator.next()?
对使用增强for循环反编译发现:
Set<String> keys = map.keySet();
Iterator iterator = keys.iterator()
while(iterator.hasNext()) {
String key = (String)iterator.next();
System.out.println("key:" + key + " value:" (String)map.get(key));
}
增强for循环根本还是iterator()
而KeySet定义的iterator()返回值为new KeyIterator(),因此在使用增强for循环遍历map.keySet()时,调用的是KeyIterator.next()。
总结:
- keySet和values和entrySet本质既然一样,就可以通过封装其相同的部分(也就是这里的HashIterator),再各自实现最重要的next方法。
- keySet()方法返回一个内部引用,并指向一个内部类对象,该内部类重写了迭代器方法,当在增强for循环时才调用,并从外部类的table中取值。
版权声明:本文为u011151359原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。