一 特性

  • 存储的内容也是键值对,可以为null
  • 当其中某个键不再使用的时候,会被GC回收掉,也就是其映射关系不能影响垃圾回收器的丢弃
  • 通过WeakReference 和ReferenceQueue 实现的,队列中会保存被GC回收的弱键
  • 非同步的,但是可以使用Collections.synchronizedMap来创造同步的
  • 同样是采用链表解决hash冲突的问题

    二 属性

    1. /**
    2. * The default initial capacity -- MUST be a power of two. 默认大小
    3. */
    4. private static final int DEFAULT_INITIAL_CAPACITY = 16;
    5. /**
    6. * The maximum capacity, used if a higher value is implicitly specified
    7. * by either of the constructors with arguments.
    8. * MUST be a power of two <= 1<<30.
    9. */
    10. private static final int MAXIMUM_CAPACITY = 1 << 30;
    11. /**
    12. * The load factor used when none specified in constructor. 负载因子
    13. */
    14. private static final float DEFAULT_LOAD_FACTOR = 0.75f;
    15. /**
    16. * The table, resized as necessary. Length MUST Always be a power of two. 2的整数幂的大小的数组
    17. */
    18. Entry<K,V>[] table;
    19. /**
    20. * The number of key-value mappings contained in this weak hash map. 实际存储的大小
    21. */
    22. private int size;
    23. /**
    24. * The next size value at which to resize (capacity * load factor). 当容量到达这个值的时候会进行扩容
    25. */
    26. private int threshold;
    27. /**
    28. * The load factor for the hash table.
    29. */
    30. private final float loadFactor;
    31. /**
    32. * Reference queue for cleared WeakEntries
    33. */
    34. private final ReferenceQueue<Object> queue = new ReferenceQueue<>();
    35. /**
    36. * The number of times this WeakHashMap has been structurally modified.
    37. * Structural modifications are those that change the number of
    38. * mappings in the map or otherwise modify its internal structure
    39. * (e.g., rehash). This field is used to make iterators on
    40. * Collection-views of the map fail-fast.
    41. *
    42. * @see ConcurrentModificationException
    43. */
    44. int modCount;

    三 重要方法

  • hash

    • 会获取键的hashCode 然后通过一些操作获得对
  • indexFor
    • 获得给定hashcode的位置 h & (length-1) 经过处理的hash码 按位与 长度减1
  • resize

    • 扩容方法

      四 添加

      1. public V put(K key, V value) {
      2. Object k = maskNull(key);
      3. int h = hash(k);
      4. Entry<K,V>[] tab = getTable();
      5. //获得对应的位置
      6. int i = indexFor(h, tab.length);
      7. //循环对应位置的链表,如果已经存在则替换
      8. for (Entry<K,V> e = tab[i]; e != null; e = e.next) {
      9. if (h == e.hash && eq(k, e.get())) {
      10. V oldValue = e.value;
      11. if (value != oldValue)
      12. e.value = value;
      13. return oldValue;
      14. }
      15. }
      16. modCount++;
      17. Entry<K,V> e = tab[i];
      18. //这里注意是采用的头插法
      19. tab[i] = new Entry<>(k, value, queue, h, e);
      20. if (++size >= threshold)
      21. resize(tab.length * 2);
      22. return null;
      23. }

      五 删除

      1. public V remove(Object key) {
      2. Object k = maskNull(key);
      3. int h = hash(k);
      4. Entry<K,V>[] tab = getTable();
      5. int i = indexFor(h, tab.length);
      6. Entry<K,V> prev = tab[i];
      7. Entry<K,V> e = prev;
      8. while (e != null) {
      9. Entry<K,V> next = e.next;
      10. if (h == e.hash && eq(k, e.get())) {
      11. modCount++;
      12. size--;
      13. if (prev == e)
      14. tab[i] = next;
      15. else
      16. prev.next = next;
      17. return e.value;
      18. }
      19. prev = e;
      20. e = next;
      21. }
      22. return null;
      23. }

      六 获取

      1. public V get(Object key) {
      2. Object k = maskNull(key);
      3. int h = hash(k);
      4. Entry<K,V>[] tab = getTable();
      5. int index = indexFor(h, tab.length);
      6. Entry<K,V> e = tab[index];
      7. while (e != null) {
      8. if (e.hash == h && eq(k, e.get()))
      9. return e.value;
      10. e = e.next;
      11. }
      12. return null;
      13. }

      七 hash 扰乱函数

      1. final int hash(Object k) {
      2. int h = k.hashCode();
      3. // This function ensures that hashCodes that differ only by
      4. // constant multiples at each bit position have a bounded
      5. // number of collisions (approximately 8 at default load factor).
      6. // 先无符号右移20位 然后在异或 无符号右移12位 在于hash进行异或
      7. h ^= (h >>> 20) ^ (h >>> 12);
      8. //再无符号右移 7 和4
      9. return h ^ (h >>> 7) ^ (h >>> 4);
      10. }

      目前还没搞懂为啥这么操作,但是肯定是为了降低hash冲突

      八 resize扩容

      ```java //put 调用的时候传参是 数组的2倍大小
      // 注意这个在删除元素的时候没有进行调用 void resize(int newCapacity) { Entry[] oldTable = getTable(); int oldCapacity = oldTable.length; if (oldCapacity == MAXIMUM_CAPACITY) {

      1. threshold = Integer.MAX_VALUE;
      2. return;

      }

      Entry[] newTable = newTable(newCapacity); transfer(oldTable, newTable); table = newTable;

      /*

      1. * If ignoring null elements and processing ref queue caused massive
      2. * shrinkage, then restore old table. This should be rare, but avoids
      3. * unbounded expansion of garbage-filled tables.
      4. */

      if (size >= threshold / 2) {

      1. threshold = (int)(newCapacity * loadFactor);

      } else {

      1. expungeStaleEntries();
      2. transfer(newTable, oldTable);
      3. table = oldTable;

      } }

  1. private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest) {
  2. for (int j = 0; j < src.length; ++j) {
  3. Entry<K,V> e = src[j];
  4. src[j] = null;
  5. while (e != null) {
  6. Entry<K,V> next = e.next;
  7. Object key = e.get();
  8. if (key == null) {
  9. e.next = null; // Help GC
  10. e.value = null; // " "
  11. size--;
  12. } else {
  13. //注意这里会使用新的长度为每个元素重新计算hash槽
  14. int i = indexFor(e.hash, dest.length);
  15. e.next = dest[i];
  16. dest[i] = e;
  17. }
  18. e = next;
  19. }
  20. }
  21. }

```