android开发-Hashmap源码解析

来源:互联网 发布:负反馈放大器实验数据 编辑:程序博客网 时间:2024/06/02 05:35
HashMap的特性
如HashMap可以接受null键值和值,而Hashtable则不能;
HashMap是非synchronized;
HashMap很快;以及HashMap储存的是键值对等等

    int DEFAULT_INITIAL_CAPACITY =4:默认的初始容量为4
  int MAXIMUM_CAPACITY = 1 << 30:最大的容量为 2 ^ 30
  float DEFAULT_LOAD_FACTOR = 0.75f:默认的加载因子为 0.75f
  Entry< K,V>[] table:Entry类型的数组,HashMap用这个来维护内部的数据结构,它的长度由容量决定
  int size:HashMap的大小
  int threshold:HashMap的极限容量,扩容临界点(容量和加载因子的乘积)


数组+链表 数组是链表的头,通过hash算法,能最快确定他存在数组的哪个位置每个元素都是HashMapEntry
HashMapEntry

     final K key;
        V value;
        final int hash;
        HashMapEntry<K, V> next;

包含了K value k的hash值 以及下一个HashMapEntry的对象

1、put方法

如果新来一个元素,获取k值的hashcode值,再把这个hashcode的值通过hash散列算法获取一个叫hash的int值
通过int index = hash & (tab.length - 1);获取指定的位置
确定一个位置之后
然后遍历这个位置上的链表,去找看看这个位置上是否有数据,如果有数据就拿出来替换上去,然后旧的值
for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
            if (e.hash == hash && key.equals(e.key)) {
                preModify(e);
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }
如果没有值,那么将值进行增加
if (size++ > threshold) {
            tab = doubleCapacity();
            index = hash & (tab.length - 1);
}
如果值大于扩容的极限,那么进行扩容,并且把新的index位置拿过来
然后再把元素添加进去 addNewEntry(key, value, hash, index);这个方法的实现只有一行代码

table[index] = new HashMapEntry<K, V>(key, value, hash, table[index]);


2、get方法

public V get(Object key) {
        if (key == null) {
            HashMapEntry<K, V> e = entryForNullKey;
            return e == null ? null : e.value;
        }

        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) {
            K eKey = e.key;
            if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                return e.value;
            }
        }
        return null;
    }
获取这个key的hash值,然后获取这个位置上的元素,然后遍历这个元素
判断key相同的(==是判断内存地址是否相同的)或者hash值相同,值也相同的(equal判断值相同的)

3、doubleCapacity方法
首先判断是否达到最大的容量
如果达到了 那么就返回原来的数组
如果没有那么就生成一个2倍大小的新数组
计算index不是像put中的那样使用hash & newCapacity - 1,而是使用hash & oldCapacity得到hightBit,再用hightBit | j得到index。二者的结果相同

 private HashMapEntry<K, V>[] doubleCapacity() {
        HashMapEntry<K, V>[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            return oldTable;
        }
        int newCapacity = oldCapacity * 2;
        HashMapEntry<K, V>[] newTable = makeTable(newCapacity);
        if (size == 0) {
            return newTable;
        }

        for (int j = 0; j < oldCapacity; j++) {
            /*
             * Rehash the bucket using the minimum number of field writes.
             * This is the most subtle and delicate code in the class.
             */
            HashMapEntry<K, V> e = oldTable[j];
            if (e == null) {
                continue;
            }
            int highBit = e.hash & oldCapacity;
            HashMapEntry<K, V> broken = null;
            newTable[j | highBit] = e;
            for (HashMapEntry<K, V> n = e.next; n != null; e = n, n = n.next) {
                int nextHighBit = n.hash & oldCapacity;
                if (nextHighBit != highBit) {
                    if (broken == null)
                        newTable[j | nextHighBit] = n;
                    else
                        broken.next = n;
                    broken = e;
                    highBit = nextHighBit;
                }
            }
            if (broken != null)
                broken.next = null;
        }
        return newTable;
    }



HashMap的特性
如HashMap可以接受null键值和值,而Hashtable则不能;
HashMap是非synchronized;
HashMap很快;以及HashMap储存的是键值对等等

    int DEFAULT_INITIAL_CAPACITY =4:默认的初始容量为4
  int MAXIMUM_CAPACITY = 1 << 30:最大的容量为 2 ^ 30
  float DEFAULT_LOAD_FACTOR = 0.75f:默认的加载因子为 0.75f
  Entry< K,V>[] table:Entry类型的数组,HashMap用这个来维护内部的数据结构,它的长度由容量决定
  int size:HashMap的大小
  int threshold:HashMap的极限容量,扩容临界点(容量和加载因子的乘积)


数组+链表 数组是链表的头,通过hash算法,能最快确定他存在数组的哪个位置每个元素都是HashMapEntry
HashMapEntry

     final K key;
        V value;
        final int hash;
        HashMapEntry<K, V> next;

包含了K value k的hash值 以及下一个HashMapEntry的对象

1、put方法

如果新来一个元素,获取k值的hashcode值,再把这个hashcode的值通过hash散列算法获取一个叫hash的int值
通过int index = hash & (tab.length - 1);获取指定的位置
确定一个位置之后
然后遍历这个位置上的链表,去找看看这个位置上是否有数据,如果有数据就拿出来替换上去,然后旧的值
for (HashMapEntry<K, V> e = tab[index]; e != null; e = e.next) {
            if (e.hash == hash && key.equals(e.key)) {
                preModify(e);
                V oldValue = e.value;
                e.value = value;
                return oldValue;
            }
        }
如果没有值,那么将值进行增加
if (size++ > threshold) {
            tab = doubleCapacity();
            index = hash & (tab.length - 1);
}
如果值大于扩容的极限,那么进行扩容,并且把新的index位置拿过来
然后再把元素添加进去 addNewEntry(key, value, hash, index);这个方法的实现只有一行代码

table[index] = new HashMapEntry<K, V>(key, value, hash, table[index]);


2、get方法

public V get(Object key) {
        if (key == null) {
            HashMapEntry<K, V> e = entryForNullKey;
            return e == null ? null : e.value;
        }

        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) {
            K eKey = e.key;
            if (eKey == key || (e.hash == hash && key.equals(eKey))) {
                return e.value;
            }
        }
        return null;
    }
获取这个key的hash值,然后获取这个位置上的元素,然后遍历这个元素
判断key相同的(==是判断内存地址是否相同的)或者hash值相同,值也相同的(equal判断值相同的)

3、doubleCapacity方法
首先判断是否达到最大的容量
如果达到了 那么就返回原来的数组
如果没有那么就生成一个2倍大小的新数组
计算index不是像put中的那样使用hash & newCapacity - 1,而是使用hash & oldCapacity得到hightBit,再用hightBit | j得到index。二者的结果相同

 private HashMapEntry<K, V>[] doubleCapacity() {
        HashMapEntry<K, V>[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            return oldTable;
        }
        int newCapacity = oldCapacity * 2;
        HashMapEntry<K, V>[] newTable = makeTable(newCapacity);
        if (size == 0) {
            return newTable;
        }

        for (int j = 0; j < oldCapacity; j++) {
            /*
             * Rehash the bucket using the minimum number of field writes.
             * This is the most subtle and delicate code in the class.
             */
            HashMapEntry<K, V> e = oldTable[j];
            if (e == null) {
                continue;
            }
            int highBit = e.hash & oldCapacity;
            HashMapEntry<K, V> broken = null;
            newTable[j | highBit] = e;
            for (HashMapEntry<K, V> n = e.next; n != null; e = n, n = n.next) {
                int nextHighBit = n.hash & oldCapacity;
                if (nextHighBit != highBit) {
                    if (broken == null)
                        newTable[j | nextHighBit] = n;
                    else
                        broken.next = n;
                    broken = e;
                    highBit = nextHighBit;
                }
            }
            if (broken != null)
                broken.next = null;
        }
        return newTable;
    }

解释引自:https://www.zhihu.com/question/45989507/answer/123029035