HashMap的几种遍历

1,366 阅读3分钟

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;&emsp;//存key值
        V value;      //存value值
        Node<K,V> next; //存下一个结点的引用
 }

分析

进入keySet()方法

public Set<K> keySet() {
        Set<K> ks = keySet;
        if (ks == null) {
            ks = new KeySet();&emsp;//返回一个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);
        });
    }
}