Java集合框架:了解WeakHashMap

WeakHashMap

基于 动态数组和链表 实现的弱键映射

WeakHashMap继承关系

WeakHashMap继承了AbstractMap抽象类,拥有Map的基本操作

 WeakHashMap源码解析

WeakHashMap构造函数:

/**
 * 使用给定的初始容量和给定的负载因子构造一个新的空 WeakHashMap
 *
 * @param  initialCapacity The initial capacity of the {@code WeakHashMap}
 * @param  loadFactor      The load factor of the {@code WeakHashMap}
 * @throws IllegalArgumentException if the initial capacity is negative,
 *         or if the load factor is nonpositive.
 */
public WeakHashMap(int initialCapacity, float loadFactor) {
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Initial Capacity: "+
                                            initialCapacity);
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;

    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal Load factor: "+
                                            loadFactor);
    int capacity = 1;
    while (capacity < initialCapacity)
        capacity <<= 1; // 保证为2的幂
    table = newTable(capacity);
    this.loadFactor = loadFactor;
    threshold = (int)(capacity * loadFactor);   // 阈值
}

/**
 * 使用给定的初始容量和默认的负载因子(0.75)构造一个新的空 WeakHashMap
 *
 * @param  initialCapacity The initial capacity of the {@code WeakHashMap}
 * @throws IllegalArgumentException if the initial capacity is negative
 */
public WeakHashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

/**
 * 使用默认的初始容量(16)和负载因子(0.75)构造一个新的空 WeakHashMap
 */
public WeakHashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}

/**
 * 使用与指定映射相同的映射构造新的@code WeakHashMap
 * 使用默认的负载因子(0.75)和足够容纳指定映射的初始容量
 *
 * @param   m the map whose mappings are to be placed in this map
 * @throws  NullPointerException if the specified map is null
 * @since   1.3
 */
public WeakHashMap(Map<? extends K, ? extends V> m) {
    this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
            DEFAULT_INITIAL_CAPACITY),
            DEFAULT_LOAD_FACTOR);
    putAll(m);
}

@SuppressWarnings("unchecked")
private Entry<K,V>[] newTable(int n) {
    return (Entry<K,V>[]) new Entry<?,?>[n];
}

/**
 * 将指定映射的所有映射复制到此映射
 * 这些映射将替换当前指定映射中任何键的映射
 *
 * @param m mappings to be stored in this map.
 * @throws  NullPointerException if the specified map is null.
 */
public void putAll(Map<? extends K, ? extends V> m) {
    int numKeysToBeAdded = m.size();    // 需添加映射的数量
    if (numKeysToBeAdded == 0)
        return;

    /*
     * 如果要添加的映射的数量>=阈值, 则展开映射
     * 若(m.size() + size) >= threshold, 但是如果要添加的键与此映射中已经存在的键重叠, 则此条件可能导致映射的容量是适当容量的两倍
     * 通过使用保守计算,我们使自己最多调整一次大小
     */
    if (numKeysToBeAdded > threshold) {
        int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
        if (targetCapacity > MAXIMUM_CAPACITY)
            targetCapacity = MAXIMUM_CAPACITY;
        int newCapacity = table.length;
        while (newCapacity < targetCapacity)
            newCapacity <<= 1;
        if (newCapacity > table.length)
            resize(newCapacity);
    }

    for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
        put(e.getKey(), e.getValue());
}

WeakHashMap删除过时条目函数:

/**
 * 从表中删除过时的条目
 */
private void expungeStaleEntries() {
    // queue:引用队列, 记录将被清除的弱条目
    for (Object x; (x = queue.poll()) != null; ) {
        synchronized (queue) {
            @SuppressWarnings("unchecked")
                Entry<K,V> e = (Entry<K,V>) x;      // 待删除条目e
            int i = indexFor(e.hash, table.length); // 获取条目索引

            Entry<K,V> prev = table[i];
            Entry<K,V> p = prev;
            while (p != null) {
                Entry<K,V> next = p.next;
                if (p == e) {
                    if (prev == e)
                        // 表示e是桶链表的第一个条目
                        // 条目prev = 被删条目e, 则删除table的条目e
                        table[i] = next;
                    else
                        prev.next = next;  // 跳过e
                    // Must not null out e.next;
                    // stale entries may be in use by a HashIterator
                    e.value = null; // Help GC
                    size--;
                    break;
                }
                prev = p;
                p = next; // 移动到下一条目, 继续比对
            }
        }
    }
}

WeakHashMap的增删/查询操作:

/**
 * 返回指定键映射到的值
 * 若此映射不包含键的映射返回null
 *
 * @see #put(Object, Object)
 */
public V get(Object key) {
    Object k = maskNull(key);
    int h = hash(k);
    Entry<K,V>[] tab = getTable();
    int index = indexFor(h, tab.length);
    Entry<K,V> e = tab[index];
    while (e != null) {
        if (e.hash == h && eq(k, e.get()))
            return e.value;
        e = e.next;
    }
    return null;
}

/**
 * 将此映射的内容重新散列到具有更大容量的新数组中
 * 当此映射中的键数达到其阈值时, 将自动调用此方法
 *
 * 如果当前容量为MAXIMUM_CAPACITY, 则此方法不调整映射的大小, 
 * 而是将threshold设置为Integer.MAX_VALUE, 这可以防止将来的调用
 *
 * @param newCapacity the new capacity, MUST be a power of two;
 *        must be greater than current capacity unless current
 *        capacity is MAXIMUM_CAPACITY (in which case value
 *        is irrelevant).
 */
void resize(int newCapacity) {
    Entry<K,V>[] oldTable = getTable(); // 获取存储数组
    int oldCapacity = oldTable.length;
    if (oldCapacity == MAXIMUM_CAPACITY) { // 旧容量达到最大容量
        threshold = Integer.MAX_VALUE;  
        return;
    }

    Entry<K,V>[] newTable = newTable(newCapacity);  // 更新最大容量
    transfer(oldTable, newTable); // 复制映射条目
    table = newTable;

    /*
     * 如果忽略空元素并处理ref队列导致大量收缩, 则恢复旧表
     * 这种情况应该很少见, 但是可以避免无限地扩展充满垃圾的表
     */
    if (size >= threshold / 2) {
        threshold = (int)(newCapacity * loadFactor);
    } else {
        expungeStaleEntries();          // 清理不引用条目
        transfer(newTable, oldTable);   // 复制元素
        table = oldTable;
    }
}

/**
 * 返回此映射中的键值映射的数目
 * 此结果是一个快照, 可能不反映未处理的条目
 * 这些条目将在下一次尝试访问之前被删除
 */
public int size() {
    if (size == 0)
        return 0;
    expungeStaleEntries();  // 删除不引用的条目
    return size;
}

/**
 * 将指定值与此映射中的指定键关联
 * 如果先前的映射包含此键的映射, 则将替换旧值
 *
 * @param key key with which the specified value is to be associated.
 * @param value value to be associated with the specified key.
 * @return the previous value associated with {@code key}, or
 *         {@code null} if there was no mapping for {@code key}.
 *         (A {@code null} return can also indicate that the map
 *         previously associated {@code null} with {@code key}.)
 */
public V put(K key, V value) {
    Object k = maskNull(key);
    int h = hash(k);
    Entry<K,V>[] tab = getTable();
    int i = indexFor(h, tab.length);

    for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
        if (h == e.hash && eq(k, e.get())) {
            V oldValue = e.value;
            if (value != oldValue)
                e.value = value;
            return oldValue;
        }
    }

    modCount++;
    Entry<K,V> e = tab[i];
    tab[i] = new Entry<>(k, value, queue, h, e);
    if (++size >= threshold)
        // 当前大小大于阈值, 扩容
        resize(tab.length * 2);
    return null;
}

/**
 * 在首次删除过时的条目后返回表
 */
private Entry<K,V>[] getTable() {
    expungeStaleEntries();
    return table;
}

WeakHashMap总结

WeakHashMap基于 数组+链表 实现的存储结构

WeakHashMap是非线程安全的

WeakHashMap允许null元素

WeakHashMap使用弱键储存键值对,弱键不被引用会放入ReferenceQueue中,然后通过GC回收

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值