JDK之IdentityHashMap源码解析

来源:互联网 发布:淘宝一个差评扣多少分 编辑:程序博客网 时间:2024/04/28 21:32

刚入java不久的程序猿,对于简单的使用已毫不满足,最终为了一探究竟,翻开了JDK的源码,以下观点为自己的理解及看了多篇博客的总结,欢迎各位大神指出不对的地方,当然也欢迎和我一样刚学的同学,一起加油努力吧~~

IdentityHashMap是什么

IdentityHashMap与HashMap一样,也是一个通过键值对来存储元素的集合,但是IdentityHashMap相对于HashMap不同的是,它能够存储key值相同的元素,并且存储方式上,数据都存储在map中,没有使用链表

IdentityHashMap源码解析

IdentityHashMap和HashMap结构相同都是继承AbstractMap,实现Map接口

public class IdentityHashMap<K,V>    extends AbstractMap<K,V>    implements Map<K,V>, java.io.Serializable, Cloneable{...}

因为与HashMap一样,这里就不做说明了,具体的可以看HashMap源码解析,下面我们直接来看IdentityHashMap的源码

    /**     * 默认容量     */    private static final int DEFAULT_CAPACITY = 32;    /**     * 最小容量     */    private static final int MINIMUM_CAPACITY = 4;    /**     * 最大容量     */    private static final int MAXIMUM_CAPACITY = 1 << 29;    /**     * 存放元素的数组     */    private transient Object[] table;    /**     * 元素的数量     */    private int size;    /**     * 修改次数     */    private transient int modCount;    /**     * 阈值(容量*加载因子)     */    private transient int threshold;    /**     * 常量NULL_KEY     */    private static final Object NULL_KEY = new Object();

了解了变量后,我们来看下IdentityHashMap的构造方法

    /**     * 无参构造,调用init方法,后面有详细说明     */    public IdentityHashMap() {        init(DEFAULT_CAPACITY);    }    /**     * 构造方法,参数为期望最大容量大小     */    public IdentityHashMap(int expectedMaxSize) {        if (expectedMaxSize < 0)            throw new IllegalArgumentException("expectedMaxSize is negative: "                                               + expectedMaxSize);        init(capacity(expectedMaxSize));    }    /**     * 构造方法,参数为map     */    public IdentityHashMap(Map<? extends K, ? extends V> m) {        // 调用构造方法        this((int) ((1 + m.size()) * 1.1));        putAll(m);    }    /**     * 通过expectedMaxSize计算容量     */    private int capacity(int expectedMaxSize) {        // 计算最小容量,大小为expectedMaxSize的1.5倍        int minCapacity = (3 * expectedMaxSize)/2;        // 实际的大小        int result;        // 当最小容量大于最大容量或者小于0时,将最大容量赋予result        if (minCapacity > MAXIMUM_CAPACITY || minCapacity < 0) {            result = MAXIMUM_CAPACITY;        } else {            // 将最小容量赋予result            result = MINIMUM_CAPACITY;            // 当result小于计算出的最小容量时,将result扩大一倍            while (result < minCapacity)                result <<= 1;        }        return result;    }    /**     * 初始化方法,initCapacity在最小与最大值之间     */    private void init(int initCapacity) {        //阈值,大小为初始化容量的三分之二        threshold = (initCapacity * 2)/3;        //表的容量为2倍的初始化容量大小        table = new Object[2 * initCapacity];    }

看完上述代码,我们发现init方法最为重要,阈值为什么要乘以三分之二?其实这个三分之二就是加载因子,初始化容量乘以加载因子算出阈值,表的容量为2倍,这是由于key,value都存于表中,而未使用链表,接下来我们继续看代码

    /**     * IdentityHashMap存入键值对的方法     */    public V put(K key, V value) {        //判断参数是否为空,返回NULL_KEY或者key        Object k = maskNull(key);        Object[] tab = table;        //数组的长度        int len = tab.length;        //根据key值和数组长度计算下标        int i = hash(k, len);        Object item;        //循环数组,元素非空时进入        while ( (item = tab[i]) != null) {            //当key与元素相等时,设置value,并取出旧的value            if (item == k) {                V oldValue = (V) tab[i + 1];                tab[i + 1] = value;                return oldValue;            }            //下一个key的下标            i = nextKeyIndex(i, len);        }        //修改次数+1        modCount++;        //无此元素时,下标i处放入key,i+1处放入value        tab[i] = k;        tab[i + 1] = value;        //当元素的长度大于阈值时候扩容        if (++size >= threshold)            //扩容方法            resize(len); // len == 2 * current capacity.        return null;    }    /**     * IdentityHashMap的get方法     */    public V get(Object key) {        //判断参数是否为空,返回NULL_KEY或者key        Object k = maskNull(key);        Object[] tab = table;        //数组的长度        int len = tab.length;        //根据key值和数组长度计算下标        int i = hash(k, len);        //循环数组,当key值与数组中一致时,取出value        while (true) {            Object item = tab[i];            if (item == k)                return (V) tab[i + 1];            if (item == null)                return null;            i = nextKeyIndex(i, len);        }    }    /**     * 判断参数是否为空,为空返回NULL_KEY,否则返回key     */    private static Object maskNull(Object key) {        return (key == null ? NULL_KEY : key);    }    /**     * 根据key值和数组长度,计算数组中的下标     */    private static int hash(Object x, int length) {        int h = System.identityHashCode(x);        //前半部分没怎么懂,后半部分是取余,至于为什么不用%length,好像是在性能方面这样更好        return ((h << 1) - (h << 8)) & (length - 1);    }    /**     * 下一个key的下标,由于下一位是value,所以这里的key为i+2     */    private static int nextKeyIndex(int i, int len) {        return (i + 2 < len ? i + 2 : 0);    }    /**     * 扩容方法     */    private void resize(int newCapacity) {        // 扩容长度为原先长度的两倍        int newLength = newCapacity * 2;        Object[] oldTable = table;        //原先数组的长度        int oldLength = oldTable.length;        if (oldLength == 2*MAXIMUM_CAPACITY) { // can't expand any further            if (threshold == MAXIMUM_CAPACITY-1)                throw new IllegalStateException("Capacity exhausted.");            threshold = MAXIMUM_CAPACITY-1;  // Gigantic map!            return;        }        if (oldLength >= newLength)            return;        //新建数组,长度为扩容后的长度        Object[] newTable = new Object[newLength];        //阈值为新建后长度的三分之一        threshold = newLength / 3;        //循环旧的table,将key,value放入新的数组        for (int j = 0; j < oldLength; j += 2) {            Object key = oldTable[j];            if (key != null) {                Object value = oldTable[j+1];                oldTable[j] = null;                oldTable[j+1] = null;                int i = hash(key, newLength);                while (newTable[i] != null)                    i = nextKeyIndex(i, newLength);                newTable[i] = key;                newTable[i + 1] = value;            }        }        table = newTable;    }

到这里,比较重要的两个方法我们就看完了,这里我们要注意的是key值后面对应的就是value,没有链表,数据都是存在table中的,细心的小伙伴可能已经发现,get方法里,取值时是引用的相同,这也就说明了为什么可以存入相同值的key,好了,下面简要的说明下剩下的方法,都比较简单

    /**     * 返回元素个数,也就是键值对数量     */    public int size() {        return size;    }    /**     * 判断是否为空     */    public boolean isEmpty() {        return size == 0;    }    /**     * 判断是否包含此key     */    public boolean containsKey(Object key) {        Object k = maskNull(key);        Object[] tab = table;        int len = tab.length;        int i = hash(k, len);        //循环时,找到返回true,没找到返回false        while (true) {            Object item = tab[i];            if (item == k)                return true;            if (item == null)                return false;            i = nextKeyIndex(i, len);        }    }    /**     * 判断是否包含此value     */    public boolean containsValue(Object value) {        Object[] tab = table;        //循环数组里的value,找到返回true,没找到返回false        for (int i = 1; i < tab.length; i += 2)            if (tab[i] == value && tab[i - 1] != null)                return true;        return false;    }    /**     * 判断是否包含键值对     */    private boolean containsMapping(Object key, Object value) {        Object k = maskNull(key);        Object[] tab = table;        int len = tab.length;        int i = hash(k, len);        while (true) {            Object item = tab[i];            if (item == k)                return tab[i + 1] == value;            if (item == null)                return false;            i = nextKeyIndex(i, len);        }    }    /**     * 将map中元素放入数组     */    public void putAll(Map<? extends K, ? extends V> m) {        int n = m.size();        if (n == 0)            return;        if (n > threshold) // conservatively pre-expand            resize(capacity(n));        for (Entry<? extends K, ? extends V> e : m.entrySet())            put(e.getKey(), e.getValue());    }    /**     * 根据key值移除键值对,返回该key对应的旧的value     */    public V remove(Object key) {        Object k = maskNull(key);        Object[] tab = table;        int len = tab.length;        int i = hash(k, len);        while (true) {            Object item = tab[i];            if (item == k) {                modCount++;                //长度-1                size--;                V oldValue = (V) tab[i + 1];                //置空                tab[i + 1] = null;                tab[i] = null;                closeDeletion(i);                return oldValue;            }            if (item == null)                return null;            i = nextKeyIndex(i, len);        }    }    /**     * 根据key与value移除键值对,返回成功或失败     */    private boolean removeMapping(Object key, Object value) {        Object k = maskNull(key);        Object[] tab = table;        int len = tab.length;        int i = hash(k, len);        while (true) {            Object item = tab[i];            if (item == k) {                if (tab[i + 1] != value)                    return false;                modCount++;                size--;                tab[i] = null;                tab[i + 1] = null;                closeDeletion(i);                return true;            }            if (item == null)                return false;            i = nextKeyIndex(i, len);        }    }    /**     * 删除后调用     */    private void closeDeletion(int d) {        // Adapted from Knuth Section 6.4 Algorithm R        Object[] tab = table;        int len = tab.length;        // Look for items to swap into newly vacated slot        // starting at index immediately following deletion,        // and continuing until a null slot is seen, indicating        // the end of a run of possibly-colliding keys.        Object item;        for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;             i = nextKeyIndex(i, len) ) {            // The following test triggers if the item at slot i (which            // hashes to be at slot r) should take the spot vacated by d.            // If so, we swap it in, and then continue with d now at the            // newly vacated i.  This process will terminate when we hit            // the null slot at the end of this run.            // The test is messy because we are using a circular table.            int r = hash(item, len);            if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) {                tab[d] = item;                tab[d + 1] = tab[i + 1];                tab[i] = null;                tab[i + 1] = null;                d = i;            }        }    }    /**     * 清空     */    public void clear() {        modCount++;        Object[] tab = table;        for (int i = 0; i < tab.length; i++)            tab[i] = null;        size = 0;    }    /**     * 比较     */    public boolean equals(Object o) {        if (o == this) {            return true;        } else if (o instanceof IdentityHashMap) {            IdentityHashMap m = (IdentityHashMap) o;            if (m.size() != size)                return false;            Object[] tab = m.table;            for (int i = 0; i < tab.length; i+=2) {                Object k = tab[i];                if (k != null && !containsMapping(k, tab[i + 1]))                    return false;            }            return true;        } else if (o instanceof Map) {            Map m = (Map)o;            return entrySet().equals(m.entrySet());        } else {            return false;  // o is not a Map        }    }

好了,到这里IdentityHashMap就看完了,感兴趣的同学可以打开源码里面还有一些内部类,迭代的东西,可以看一看,好了,就说到这里了~

0 0
原创粉丝点击