HashMap的遍历
之前对hashMap的遍历,一直只清楚怎么用,就是调用keySet()方法获取到包含它的键的Set集合.然后就调用iterator方法遍历出键值对.今天结合源码来看看它是如何实现的.
通过keySet方法
调用keySet方法返回一个存储key值的Set集合,根据key值再获取value值,然后接下来无论你通过iterator方法或者增强型的for循环也好,都能遍历出.
public class Test {
public static void main(String[] args) {
Map<Integer,String> map=new HashMap<>();
map.put(1,"a");
map.put(2,"b");
map.put(3,"c");
//调用keySet返回Set<K>
Set<Integer> set = map.keySet();
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
System.out.println(next+":"+map.get(next));
}
}
}
keySet源码分析
猜想
在没看源码前我以为它是将key值全部取出来然后存放到一个set集合里返回出来,然而结果并不是这样.
前提
在此之前先得了解一下HashMap的底层实现HashMap的存储结构其实就是Node结构,本质就是链表数组+红黑树.
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key; //存key值
V value; //存value值
Node<K,V> next; //存下一个结点的引用
}
分析
进入keySet()方法
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet(); //返回一个KeySet对象
keySet = ks;
}
return ks;
}
查看到是返回了一个对象KeySet,进入KeySet
final class KeySet extends AbstractSet<K> {
……
public final Iterator<K> iterator() { return new KeyIterator(); }
……
}
我们遍历这个Set调用的是iterator方法所以继续进入iterator方法(增强的for最终也会转换成了对iterator的调用)
final class KeyIterator extends HashIterator
implements Iterator<K> {
//看方法名 很明显就是获取下一个结点的key值
public final K next() { return nextNode().key; }
}
结论
所以看源码很明显得知,keySet遍历直接操作的是HashMap的底层结构.迭代器直接遍历Node获取key值
通过entrySet
public class Test {
public static void main(String[] args) {
Map<Integer,String> map=new HashMap<>();
map.put(1,"a");
map.put(2,"b");
map.put(3,"c");
//调用entrySet返回Map.Entry<K,V>
Set<Map.Entry<Integer, String>> set = map.entrySet();
//再通过迭代器遍历
Iterator<Map.Entry<Integer, String>> iterator = set.iterator();
while (iterator.hasNext()){
Map.Entry<Integer, String> next = iterator.next();
System.out.println(next.getKey()+":"+next.getValue());
}
}
}
entrySet源码分析
和我们分析keySet其实差不多,最终进入到iterator,发现它返回的是一个EntryIterator对象,进来看看代码,很明显就是直接返回一个Node结点.发现entrySet也是直接操作底层Node结点.
final class EntryIterator extends HashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
//Node的实现了Map接口中的Entry接口.Entry接口中就是一些获取key值和value值的方法太多就不贴出来了,需要Node自己实现
static class Node<K,V> implements Map.Entry<K,V> {
}
通过forEach进行
我们调用forEach方法发现需要传给它一个BiConsumer<? super K, ? super V> action对象,我们进入forEach方法查看它的源码
//部分代码,可以知道它每次都是取出HashMap的key和value值传给BiConsumer接口的accept方法
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key, e.value);
}
accept方法需要自己实现,原来就是传了HashMap的key和value值给你,接下来你自己发挥.给它一个实现类即可.
public class Test{
public static void main(String[] args){
Map<Integer,String> map=new HashMap<>();
map.put(1,"a");
map.put(2,"b");
map.put(3,"c");
map.forEach(new BiConsumerImpl());
}
}
class BiConsumerImpl implements BiConsumer {
@Override
public void accept(Object o, Object o2) {
System.out.println(o+":"+o2);
//打印键值
}
}
//Lambda写法更简便
public class Test{
public static void main(String[] args){
Map<Integer,String> map=new HashMap<>();
map.put(1,"a");
map.put(2,"b");
map.put(3,"c");
map.forEach((K,V)->{
System.out.println(K+":"+V);
});
}
}