HashMap从源码角度分析遍历过程

来源:互联网 发布:炉石毕游侠知乎 编辑:程序博客网 时间:2024/05/22 05:32

上一篇分析了HashMap的数据结构以及put方法的源码
HashMap源码解析,下面分析HashMap的遍历过程的源码。
遍历的方法有很多中,主要分析下面这种:

  Iterator<Map.Entry<String, String>> iterator = hashMap.entrySet().iterator();                while (iterator.hasNext()) {                    Map.Entry<String, String> next = iterator.next();                    String key = next.getKey();                    String value = next.getValue();                }

hashMap.entrySet()会new一个EntrySet对象。
调用entrySet.iterator()方法时会通过newEntryIterator()方法调用HashIterator的构造方法创建一个Iterator实例。

public Set<Map.Entry<K,V>> entrySet() {    return entrySet0();}private Set<Map.Entry<K,V>> entrySet0() {    Set<Map.Entry<K,V>> es = entrySet;    return es != null ? es : (entrySet = new EntrySet());}private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {        public Iterator<Map.Entry<K,V>> iterator() {            return newEntryIterator();        }          //..............    }Iterator<Map.Entry<K,V>> newEntryIterator()   {        return new EntryIterator();private abstract class HashIterator<E> implements Iterator<E> {        HashMapEntry<K,V> next;        // next entry to return        int expectedModCount;   // For fast-fail        int index;              // current slot        HashMapEntry<K,V> current;     // current entry        HashIterator() {            expectedModCount = modCount;            if (size > 0) { // advance to first entry                HashMapEntry[] t = table;                while (index < t.length && (next = t[index++]) == null)                    ;            }        }        public final boolean hasNext() {            return next != null;        }        final Entry<K,V> nextEntry() {            if (modCount != expectedModCount)                throw new ConcurrentModificationException();            HashMapEntry<K,V> e = next;            if (e == null)                throw new NoSuchElementException();            if ((next = e.next) == null) {                HashMapEntry[] t = table;                while (index < t.length && (next = t[index++]) == null)                    ;            }            current = e;            return e;        }        public void remove() {            if (current == null)                throw new IllegalStateException();            if (modCount != expectedModCount)                throw new ConcurrentModificationException();            Object k = current.key;            current = null;            HashMap.this.removeEntryForKey(k);            expectedModCount = modCount;        }    }

HashIterator的构造方法通过while循环遍历HashMap中的数组,找到第一个不为null的值,并赋值给next。
PS: HashMap中取数据是从数组的第0个位置开始找的,根据上篇分析put方法时,知道插入的位置时根据hash值计算出来的,所以HashMap取出数据的顺序和插入的顺序是不一致的。

  HashIterator() {            expectedModCount = modCount;            if (size > 0) { // advance to first entry                HashMapEntry[] t = table;                while (index < t.length && (next = t[index++]) == null)                    ;            }        }

通过hashMap.entrySet().iterator()已经找到了第一个不为null的元素,如果hasNext()返回false表示没有别的元素,返回true调用iterator.next()方法,此方法最终调用nextEntry()。

nextEntry将构造方法中找到的next返回,并通过next节点中存储的下一个节点的指针去找下个数据,如果下一个数据为null,则表示该链表已经到最尾部了,则继续查找数组下一个位置存储的链表,直到不为null,或者遍历结束。

 public final boolean hasNext() {            return next != null;        }  final Entry<K,V> nextEntry() {            if (modCount != expectedModCount)                throw new ConcurrentModificationException();                //将next 赋值给e 当作返回值使用            HashMapEntry<K,V> e = next;            if (e == null)                throw new NoSuchElementException();    //链表结构存储了下个节点的指针,可以直接取             if ((next = e.next) == null) {     //当前节点是链表中最后一个元素,从数组下一个位置中查找不为null的元素                HashMapEntry[] t = table;                while (index < t.length && (next = t[index++]) == null)                    ;            }            current = e;            return e;        }

调用hashMap.get(“4”);取数据,流程和调用put方法保存数据相同,都是通过key的值得到hash值然后找到数组中的位置,之后遍历链表得到Entry。找到Entry之后调用getKey和getValue就非常简单了。

 final Entry<K,V> getEntry(Object key) {        if (size == 0) {            return null;        }        int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);        for (HashMapEntry<K,V> e = table[indexFor(hash, table.length)];             e != null;             e = e.next) {            Object k;            if (e.hash == hash &&                ((k = e.key) == key || (key != null && key.equals(k))))                return e;        }        return null;    }

这里HashMap的遍历就分析完了。