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

上一篇分析了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的遍历就分析完了。

    原文作者:长腿欧巴的痘痘
    原文地址: https://blog.csdn.net/DZ266912/article/details/78233777
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞