美文网首页
白话HashMap源码(下)

白话HashMap源码(下)

作者: s1991721 | 来源:发表于2018-05-08 14:50 被阅读0次

    上一篇白话HashMap源码(上)看过了HashMap的基本结构和主要的操作方法Get、Put、Remove的源码。了解了HashMap的存储方式,数组加链表,与空间的自增长思路,每次空间翻倍旧数据重新装填。

    这一篇主要看一下遍历相关的源码。

    遍历前需要了解的方法

    containsKey

        @Override public boolean containsKey(Object key) {
            if (key == null) {//key为null的条件
                return entryForNullKey != null;
            }
    
            int hash = Collections.secondaryHash(key);
            HashMapEntry<K, V>[] tab = table;
            for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];
                    e != null; e = e.next) {//由key找到数组内的具体项,遍历链表
                K eKey = e.key;
                if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                    return true;
                }
            }
            return false;
        }
    

    containsValue

    横向遍历数组,纵向遍历链表,如果没有找到别忘了HashMap还维护了一个entryForNullKey。

    但是区分了value为null的条件,因为null.equals会报错。

        @Override public boolean containsValue(Object value) {
            HashMapEntry[] tab = table;
            int len = tab.length;
            if (value == null) {
                for (int i = 0; i < len; i++) {
                    for (HashMapEntry e = tab[i]; e != null; e = e.next) {
                        if (e.value == null) {
                            return true;
                        }
                    }
                }
                return entryForNullKey != null && entryForNullKey.value == null;
            }
    
            // value is non-null
            for (int i = 0; i < len; i++) {
                for (HashMapEntry e = tab[i]; e != null; e = e.next) {
                    if (value.equals(e.value)) {
                        return true;
                    }
                }
            }
            return entryForNullKey != null && value.equals(entryForNullKey.value);
        }
    

    遍历keySet

    由于keySet、values、entrySet的实现逻辑一样,此处就只看keySet的源码

    第一步返回Set<K>

        @Override public Set<K> keySet() {
            Set<K> ks = keySet;
            return (ks != null) ? ks : (keySet = new KeySet());
        }
    

    可以看到返回了一个KeySet对象

        private final class KeySet extends AbstractSet<K> {
            public Iterator<K> iterator() {
                return newKeyIterator();
            }
            public int size() {
                return size;
            }
            public boolean isEmpty() {
                return size == 0;
            }
            public boolean contains(Object o) {
                return containsKey(o);
            }
            public boolean remove(Object o) {
                int oldSize = size;
                HashMap.this.remove(o);
                return size != oldSize;
            }
            public void clear() {
                HashMap.this.clear();
            }
        }
    

    里面包含了常用的方法,size方法直接用HashMap对象的size,isEmpty一样,contains则是使用HashMap的containsKey方法,remove也是调用HashMap对象的方法,关键的是iterator这个迭代器。

    第二步迭代器

    Iterator<K> newKeyIterator() { return new KeyIterator();   }
    

    源码很简单,直接返回KeyIterator对象,继续向下走。

        private final class KeyIterator extends HashIterator
                implements Iterator<K> {
            public K next() { return nextEntry().key; }
        }
    

    返回了nextEntry.key,看名字应该是下一项,但nextEntry哪里来的呢?

    看KeyIterator 的继承,HashIterator它是整个HashMap迭代的实现者

    第三步迭代的实现者

        private abstract class HashIterator {
            int nextIndex;
            HashMapEntry<K, V> nextEntry = entryForNullKey;
            HashMapEntry<K, V> lastEntryReturned;
            int expectedModCount = modCount;
    
            HashIterator() {//初始时,nextEntry指到第一项
                if (nextEntry == null) {
                    HashMapEntry<K, V>[] tab = table;
                    HashMapEntry<K, V> next = null;
                    while (next == null && nextIndex < tab.length) {
                        next = tab[nextIndex++];
                    }
                    nextEntry = next;
                }
            }
    
            public boolean hasNext() {
                return nextEntry != null;
            }
    
            HashMapEntry<K, V> nextEntry() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
                if (nextEntry == null)
                    throw new NoSuchElementException();
    
                HashMapEntry<K, V> entryToReturn = nextEntry;
                HashMapEntry<K, V>[] tab = table;
                HashMapEntry<K, V> next = entryToReturn.next;
                //如果这一项是链表,则返回链表的下一项,否则返回数组的下一项
                while (next == null && nextIndex < tab.length) {
                    next = tab[nextIndex++];
                }
                nextEntry = next;
                return lastEntryReturned = entryToReturn;
            }
    
            public void remove() {
                if (lastEntryReturned == null)
                    throw new IllegalStateException();
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
                HashMap.this.remove(lastEntryReturned.key);
                lastEntryReturned = null;
                expectedModCount = modCount;
            }
        }
    

    至此HashMap的源码就看完了,至于其他功能类的方法,相信有了这两篇的基础想看懂原理应该不难。

    HashSet的内部实现其实也是HashMap,只是HashSet存入的value作为key,HashSet对象作为value存入HashMap。

    相关文章

      网友评论

          本文标题:白话HashMap源码(下)

          本文链接:https://www.haomeiwen.com/subject/knnirftx.html