Java-Collection源码分析(十一)—— IdentityHashMap

 IdentityHashMap

继承了AbstractMap类实现了Map 接口。

主要有两方面的特点:

在数据结构方面:将key与value值相继保存在hash表中,如下图所示

在查找数据方面:value值采用的“==”的方式,而不是“equals”的方式。

源码分析(数据结构与实现方法):

public class IdentityHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, java.io.Serializable, Cloneable
{
    //no-args构造函数使用的初始容量。 必须是两个的力量。 
    //值32对应于(指定)预期的最大大小为21,给定的负载因子为2/3。
    private static final int DEFAULT_CAPACITY = 32;

    //如果由具有参数的任何构造函数隐式指定较低值,则使用最小容量。 
    //给定的负载因子为2/3,值4对应于预期的最大尺寸2。 必须是两个的力量。
    private static final int MINIMUM_CAPACITY = 4;

    //如果由具有参数的任何构造函数隐式指定较高值,则使用的最大容量。 必须是两个<= 1 << 29的幂。
    //事实上,map可以保存不超过MAXIMUM_CAPACITY-1个项目,因为它必须至少有一个slot == null的插槽,
    //以避免get(),put(),remove()中的无限循环)
    private static final int MAXIMUM_CAPACITY = 1 << 29;

    //表必要时调整大小。 长度必须始终是2的幂。
    transient Object[] table; // non-private to simplify nested class access

    //包含在该身份哈希映射中的键值映射的数量。
    int size;

    //修改的次数,以支持快速故障迭代器
    transient int modCount;

    //值表示表中的空key
    static final Object NULL_KEY = new Object();

    //如果空值为NULL,则使用NULL_KEY。
    private static Object maskNull(Object key) {
        return (key == null ? NULL_KEY : key);
    }

    //将null key的内部表示形式返回给caller作为null。
    static final Object unmaskNull(Object key) {
        return (key == NULL_KEY ? null : key);
    }

    //构造一个带有默认预期最大大小(21)的新的空标识哈希映射
    public IdentityHashMap() {
        init(DEFAULT_CAPACITY);
    }

    //构造一个具有指定的预期最大大小的新的空白地图。
    //将超过预期数量的键值映射到地图中可能会导致内部数据结构增长,这可能会有点耗时。
    public IdentityHashMap(int expectedMaxSize) {
        if (expectedMaxSize < 0)
            throw new IllegalArgumentException("expectedMaxSize is negative: "
                                               + expectedMaxSize);
        init(capacity(expectedMaxSize));
    }

    //返回给定预期最大大小的适当容量。 返回MINIMUM_CAPACITY与MAXIMUM_CAPACITY之间的最小值
    //(包括maxIMUM_CAPACITY),大于(3 * expectedMaxSize)/ 2,如果存在这样的数字。 否则返回MAXIMUM_CAPACITY。
    private static int capacity(int expectedMaxSize) {
        // assert expectedMaxSize >= 0;
        return
            (expectedMaxSize > MAXIMUM_CAPACITY / 3) ? MAXIMUM_CAPACITY :
            (expectedMaxSize <= 2 * MINIMUM_CAPACITY / 3) ? MINIMUM_CAPACITY :
            Integer.highestOneBit(expectedMaxSize + (expectedMaxSize << 1));
    }

    //将对象初始化为具有指定初始容量的空map,假定其为MINIMUM_CAPACITY与MAXIMUM_CAPACITY(含最大值)之间的两倍。
    private void init(int initCapacity) {
        // assert (initCapacity & -initCapacity) == initCapacity; // power of 2
        // assert initCapacity >= MINIMUM_CAPACITY;
        // assert initCapacity <= MAXIMUM_CAPACITY;

        table = new Object[2 * initCapacity];
    }

    //构造一个新的标识哈希映射,其中包含指定映射中的key-value映射。
    public IdentityHashMap(Map<? extends K, ? extends V> m) {
        //允许一点点增加
        this((int) ((1 + m.size()) * 1.1));
        putAll(m);
    }

    //返回此标识哈希映射中的key-value映射的数量。
    public int size() {
        return size;
    }

    //如果此标识哈希映射不包含key-value映射,则返回true。
    public boolean isEmpty() {
        return size == 0;
    }

    //返回Object x的索引。
    private static int hash(Object x, int length) {
        int h = System.identityHashCode(x);
        //乘以-127,左移使用最少位作为哈希的一部分
        return ((h << 1) - (h << 8)) & (length - 1);
    }

    //计算下一个键的位置,因为IdentityHashMap是通过键值各占一个桶来存储的,所以计算下一个键的位置是 i+2,而不是 i+1。
    private static int nextKeyIndex(int i, int len) {
        return (i + 2 < len ? i + 2 : 0);
    }

    //返回指定键映射到的值,如果此映射不包含该键的映射,则返回null。
    public V get(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)
                return (V) tab[i + 1]; //返回value的值
            if (item == null)
                return null;
            i = nextKeyIndex(i, len);	//指向下一个键的位置
        }
    }

    //测试此标识哈希映射中指定的对象引用是否为密钥。
    public boolean containsKey(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)
                return true;
            if (item == null)
                return false;
            i = nextKeyIndex(i, len);
        }
    }

    //测试指定的对象引用是否是此标识哈希映射中的值。
    public boolean containsValue(Object value) {
        Object[] tab = table;
        for (int i = 1; i < tab.length; i += 2)
        		//采用的是"=="的方式,而不是采用equals方式
            if (tab[i] == value && tab[i - 1] != null)
                return true;
        return false;
    }
		//测试指定的key-value映射是否在map中。
    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);
        }
    }

    //将指定的value与此标识哈希映射中的指定key相关联。 如果map先前包含了该键的映射,则替换旧值。
    public V put(K key, V value) {
        final Object k = maskNull(key);
        retryAfterResize: for (;;) {
            final Object[] tab = table;
            final int len = tab.length;
            int i = hash(k, len);

            for (Object item; (item = tab[i]) != null;i = nextKeyIndex(i, len)) {
                if (item == k) {
                    @SuppressWarnings("unchecked")
                        V oldValue = (V) tab[i + 1];
                    tab[i + 1] = value;
                    return oldValue;
                }
            }

            final int s = size + 1;
            if (s + (s << 1) > len && resize(len))
                continue retryAfterResize;
            modCount++;
            tab[i] = k;
            tab[i + 1] = value;
            size = s;
            return null;
        }
    }

    //如果需要,调整表的大小以保持给定的容量。
    private boolean 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 (size == MAXIMUM_CAPACITY - 1)
                throw new IllegalStateException("Capacity exhausted.");
            return false;
        }
        if (oldLength >= newLength)
            return false;

        Object[] newTable = new Object[newLength];

        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;
        return true;
    }

    //将指定map的所有映射复制到此map。 这些映射将替换此映射对当前指定映射中的任何键的任何映射。
    public void putAll(Map<? extends K, ? extends V> m) {
        int n = m.size();
        if (n == 0)
            return;
        if (n > size)
            resize(capacity(n)); // conservatively pre-expand
        for (Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

    //从该mpa中删除此key的映射(如果存在)。
    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++;
                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);
        }
    }

    //如果存在,则从map地图中删除指定的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);
        }
    }

    //在删除后重新整理所有可能相关的条目。 这保留了get,put等所需的线性探针碰撞属性。
    private void closeDeletion(int d) {
        // Adapted from Knuth Section 6.4 Algorithm R
        Object[] tab = table;
        int len = tab.length;

        // 查找项目,以便在删除后立即从索引开始交换到新离开的插槽中,
        //并继续直到看到空插槽,指示运行可能相冲突的键的结束。
        Object item;
        for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;i = nextKeyIndex(i, len) ) {
            //如果槽i上的项目(其中哈希处于槽位r)应该由d空出位置,
            //则触发以下测试。 如果是这样,我们交换它,然后继续d现在在新离职我。 
            //当我们在该运行结束时打到空槽时,这个过程将会终止。测试是凌乱的,因为我们使用循环表。
            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;
            }
        }
    }

    //从此map中删除所有的映射。此调用返回后,地图将为空。
    public void clear() {
        modCount++;
        Object[] tab = table;
        for (int i = 0; i < tab.length; i++)
            tab[i] = null;
        size = 0;
    }

    //将指定的对象与此映射进行比较以获得相等性。 
    //如果给定的对象也是一个映射,并且两个映射表示相同的对象引用映射,则返回true。 
    //更正式地,如果且仅当this.entrySet()。equals(m.entrySet()),则此映射等于另一个映射m。
		//由于该映射的基于参考平等的语义,如果将该映射与法线映射进行比较,
		//则可能会违反Object.equals合同的对称性和传递性要求。 
		//然而,Object.equals合同保证在IdentityHashMap实例中保存。
    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
        }
    }

    //返回此身份哈希映射的浅拷贝:键和值本身不被克隆。
    public Object clone() {
        try {
            IdentityHashMap<?,?> m = (IdentityHashMap<?,?>) super.clone();
            m.entrySet = null;
            m.table = table.clone();
            return m;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }
		//将每个条目的值替换为对该条目调用给定函数的结果,直到所有条目都被处理或该函数抛出异常。
    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        int expectedModCount = modCount;

        Object[] t = table;
        for (int index = 0; index < t.length; index += 2) {
            Object k = t[index];
            if (k != null) {
                t[index + 1] = function.apply((K) unmaskNull(k), (V) t[index + 1]);
            }

            if (modCount != expectedModCount) {
                throw new ConcurrentModificationException();
            }
        }
    }

}
数据结构操作:

put():

remove():



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值