Hashmap源码解析 keyset,entryset

Hashmap源码解析 keyset,entryset

HashMap 继承于AbstractMap,
同时实现了Map接口:属于Map的子类。
Cloneable接口:可以被复制。
Serializable:可以被序列化。

HashMap是一个键值对的集合,通常通过
HashMap.put(K,V)添加单个元素。
本文主要讨论Hashmap中的实现细节,put函数,keyset和entryset函数。

/**
     * An empty table instance to share when the table is not inflated.
     */
    static final HashMapEntry<?,?>[] EMPTY_TABLE = {};

    /**
     * The table, resized as necessary. Length MUST Always be a power of two.
     */
    transient HashMapEntry<K,V>[] table = (HashMapEntry<K,V>[]) EMPTY_TABLE;

首先构造了一个为空的名为table 的HashMapEntry的数组,这里将名为EMPTY_TABLE的空数组的对象赋给了table (不知道这里这样赋值的意义)

既然这里创建了HashMapEntry,我们来看一下这个类是什么

static class HashMapEntry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        HashMapEntry<K,V> next;
        int hash;

        /**
         * Creates new entry.
         */
        HashMapEntry(int h, K k, V v, HashMapEntry<K,V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        public final K getKey() {
            return key;
        }

        public final V getValue() {
            return value;
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry)o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2)))
                    return true;
            }
            return false;
        }

        public final int hashCode() {
            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
        }

        public final String toString() {
            return getKey() + "=" + getValue();
        }

        /**
         * This method is invoked whenever the value in an entry is
         * overwritten by an invocation of put(k,v) for a key k that's already
         * in the HashMap.
         */
        void recordAccess(HashMap<K,V> m) {
        }

        /**
         * This method is invoked whenever the entry is
         * removed from the table.
         */
        void recordRemoval(HashMap<K,V> m) {
        }
    }

实质上是一个Hashmap中的内部类,这里的k和v实际就是我们传入的键值对的值,值得注意的是其中的变量HashMapEntry

put方法

public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);   //扩充数组
        }
        if (key == null)   //对key进行判断
            return putForNullKey(value);
        int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);  //得到hash值
        int i = indexFor(hash, table.length);                        //通过hash值得到具体的位置
        for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {   //判断该HashMapEntry是否为空
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {不为空的情况下判断是否需要进行替换
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);             //如果没有进行替换,直接进行添加操作
        return null;
    }

同样的,进入put方法时候需要对数组的大小进行判断,如果为空的化,就会动态申请数组,这里和LinkedList一致,因为是利用数组实现的。
接着对key进行判空,如果为空的化就执行putForNullKey方法。

 private V putForNullKey(V value) {
        for (HashMapEntry<K,V> e = table[0]; e != null; e = e.next) {   //默认将key为null填入第一个,所以这里进行判断,将key为空的value替换掉
            if (e.key == null) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(0, null, value, 0);   //如果第一次添加key为null的HashMapEntry,或者如果判断的前面的HashMapEntry都不为空,同时其key也不为空时候会执行添加操作,同样默认将其添加到第一个位置
        return null;
    }

这里首先从第一个HashMapEntry开始判断,如果key同样是空的话,就会直接替换其value,如果找到了为空的话,直接将其添加到0的位置。
接下来的解析就像上面代码那样。

keyset

keyset是map中一个用于装化为set的函数,同时也是map中的原始函数,这里同样贴出keyset的代码

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

可以看到只有短短的一行,将ks的引用指向keySet,同时判断keySet是否为空,如果为空的话,就对其进行new KeySet()操作。接着我们看new KeySet()类中的操作

private final class KeySet extends AbstractSet<K> {
        public Iterator<K> iterator() {    //实现了iterator实例
            return newKeyIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsKey(o);
        }
        public boolean remove(Object o) {
            return HashMap.this.removeEntryForKey(o) != null;
        }
        public void clear() {
            HashMap.this.clear();
        }
        public final Spliterator<K> spliterator() {
            return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
        }
        public final void forEach(Consumer<? super K> action) {      //java8中Lambda表达式部分
            HashMapEntry<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
                        action.accept(e.key);
                        // Android-modified - this was outside of the loop, inconsistent with other
                        // collections
                        if (modCount != mc) {
                            throw new ConcurrentModificationException();
                        }
                    }
                }

            }
        }
    }

这里我们着重看下面的代码,层层调用最后到了HashIterator中

public Iterator<K> iterator() {    //实现了iterator实例
            return newKeyIterator();    //实际返回了一个KeyIterator 
        }

这里实现了实现了iterator实例,也就是其可以通过foreach对其进行遍历,我们继续查看

Iterator<K> newKeyIterator()   {
        return new KeyIterator();     //返回一个KeyIterator实例
    }

private final class KeyIterator extends HashIterator<K> {    //由于KeyIterator 继承于HashIterator。所以可以直接调用其方法。
        public K next() {                     //重写了next方法
            return nextEntry().getKey();      //next方法直接定位到了nextEntry方法中
        }


 private abstract class HashIterator<E> implements Iterator<E> {
        HashMapEntry<K,V> next;        // next entry to return            //下一个需要返回的HashMapEntry
        int expectedModCount;   // For fast-fail
        int index;              // current slot
        HashMapEntry<K,V> current;     // current entry            //当前的HashMapEntry

        HashIterator() {
            expectedModCount = modCount;
            if (size > 0) { // advance to first entry
                HashMapEntry[] t = table;
                while (index < t.length && (next = t[index++]) == null)    //构造方法中通过不断的遍历,直到找到那个不为空的HashMapEntry为止
                    ;
            }
        }

        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同样为空,说明值已经取完了,可以直接进行查找下一个HashMapEntry
                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;
        }
    }

这里我们就理清楚了,实质上其iterator是返回的HashIterator,同时在HashIterator中实现了一些具体的操作,这样就完成一个完整的iterator。

既然iterator都有了就可以进行遍历了,那这个代码最后的forEach又是什么鬼东西,传入的参数还是
Consumer< super K> action,分析下面的代码,直到他也是一个类似于遍历的函数,通过不断的遍历table中的值,同时通过action.accept将其取出。

查阅资料知道了这是java8中的Lambda表达式部分,我们通过这里重写了Lambda表达式的逻辑。这里同样是通过遍历,实现了依次取出值的目的,同时通过action.accept(e.key),得出需要取出的值。

public final void forEach(Consumer<? super K> action) {      //java8中Lambda表达式部分
            HashMapEntry<K,V>[] tab;
            if (action == null)
                throw new NullPointerException();
            if (size > 0 && (tab = table) != null) {
                int mc = modCount;
                for (int i = 0; i < tab.length; ++i) {
                    for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {   //循环找出不为空的HashMapEntry
                        action.accept(e.key);
                        // Android-modified - this was outside of the loop, inconsistent with other
                        // collections
                        if (modCount != mc) {
                            throw new ConcurrentModificationException();
                        }
                    }
                }

            }
        }

这里为了验证其重写操作,我们进行依次代码验证,这里实现了一个简单的类En ,里面一个int数组,同时重写了forEach,实现从4,3,2,1的输出和Iterator实例,实现从1,2,3,4的输出。同时编 写代码测试正确性。
可以看到程序输出4,3,2,1 和1,2,3,4。

        En en=new En();
        en.forEach(cc->Log.e(TAG,cc));    //通过Lambda表达式输出
        for (String s:en){               //通过foreach进行输出
            Log.e(TAG,s);
        }



public class En extends AbstractSet<String>{
        int a[]=new int[4];
        int flag=0;
        public En(){
            a[0]=1;
            a[1]=2;
            a[2]=3;
            a[3]=4;
        }
        @RequiresApi(api = Build.VERSION_CODES.N)
        @Override
        public void forEach(Consumer<? super String> action) {
            for (int i=3;i>=0;i--){
                action.accept(String.valueOf(a[i]));
            }

        }
        @Override
        public Iterator<String> iterator() {
            return new Iterator<String>() {
                @Override
                public boolean hasNext() {
                    if (flag<4){
                        return true;
                    }
                    return false;
                }

                @Override
                public String next() {
                    return String.valueOf(a[flag++]);
                }
            };
        }

        @Override
        public int size() {
            return 4;
        }
    }

同样我们考察entryset,发现其和 keyset本质是一样的,只是在代码中返回的变成了Set<Map.Entry<K,V>>,同时遍历时候将key和value的值同时存入其中,有兴趣可以下去讨论下。

猜你喜欢

转载自blog.csdn.net/zxc641483573/article/details/79026171