IT序号网

WeakHashMap介绍知识解答

lxf 2021年05月25日 编程语言 160 0

WeakHashMap简介

    WeakHashMap 继承于AbstractMap,实现了Map接口。
    和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null
   不过WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。
    这个“弱键”的原理呢?大致上就是,通过WeakReference和ReferenceQueue实现的。 WeakHashMap的key是“弱键”,即是WeakReference类型的;ReferenceQueue是一个队列,它会保存被GC回收的“弱键”。实现步骤是:
    (01) 新建WeakHashMap,将“键值对”添加到WeakHashMap中。
           实际上,WeakHashMap是通过数组table保存Entry(键值对);每一个Entry实际上是一个单向链表,即Entry是键值对链表。
   (02) 当某“弱键”不再被其它对象引用,并被GC回收时。在GC回收该“弱键”时,这个“弱键”也同时会被添加到ReferenceQueue(queue)队列中。
   (03) 当下一次我们需要操作WeakHashMap时,会先同步table和queue。table中保存了全部的键值对,而queue中保存被GC回收的键值对;同步它们,就是删除table中被GC回收的键值对
   这就是“弱键”如何被自动从WeakHashMap中删除的步骤了。

和HashMap一样,WeakHashMap是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap。

WeakHashMap的构造函数

 WeakHashMap共有4个构造函数,如下:

 1 // 默认构造函数。 
 2 WeakHashMap() 
 3  
 4 // 指定“容量大小”的构造函数 
 5 WeakHashMap(int capacity) 
 6  
 7 // 指定“容量大小”和“加载因子”的构造函数 
 8 WeakHashMap(int capacity, float loadFactor) 
 9  
10 // 包含“子Map”的构造函数 
11 WeakHashMap(Map<? extends K, ? extends V> map)

WeakHashMap的API

 1 void                   clear() 
 2 Object                 clone() 
 3 boolean                containsKey(Object key) 
 4 boolean                containsValue(Object value) 
 5 Set<Entry<K, V>>       entrySet() 
 6 V                      get(Object key) 
 7 boolean                isEmpty() 
 8 Set<K>                 keySet() 
 9 V                      put(K key, V value) 
10 void                   putAll(Map<? extends K, ? extends V> map) 
11 V                      remove(Object key) 
12 int                    size() 
13 Collection<V>          values()

WeakHashMap数据结构

WeakHashMap的继承关系如下

1 java.lang.Object 
2    ↳     java.util.AbstractMap<K, V> 
3          ↳     java.util.WeakHashMap<K, V> 
4  
5 public class WeakHashMap<K,V> 
6     extends AbstractMap<K,V> 
7     implements Map<K,V> {}

(01) WeakHashMap继承于AbstractMap,并且实现了Map接口。
(02) WeakHashMap是哈希表,但是它的键是"弱键"。WeakHashMap中保护几个重要的成员变量:table, size, threshold, loadFactor, modCount, queue。
  table是一个Entry[]数组类型,而Entry实际上就是一个单向链表。哈希表的"key-value键值对"都是存储在Entry数组中的。 
  size是Hashtable的大小,它是Hashtable保存的键值对的数量。 
  threshold是Hashtable的阈值,用于判断是否需要调整Hashtable的容量。threshold的值="容量*加载因子"。
  loadFactor就是加载因子。 
  modCount是用来实现fail-fast机制的
  queue保存的是“已被GC清除”的“弱引用的键”。

WeakHashMap源码解析(基于JDK1.6.0_45)

 下面对WeakHashMap的源码进行说明

  1 package java.util; 
  2 import java.lang.ref.WeakReference; 
  3 import java.lang.ref.ReferenceQueue; 
  4  
  5 public class WeakHashMap<K,V> 
  6     extends AbstractMap<K,V> 
  7     implements Map<K,V> { 
  8  
  9     // 默认的初始容量是16,必须是2的幂。 
 10     private static final int DEFAULT_INITIAL_CAPACITY = 16; 
 11  
 12     // 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换) 
 13     private static final int MAXIMUM_CAPACITY = 1 << 30; 
 14  
 15     // 默认加载因子 
 16     private static final float DEFAULT_LOAD_FACTOR = 0.75f; 
 17  
 18     // 存储数据的Entry数组,长度是2的幂。 
 19     // WeakHashMap是采用拉链法实现的,每一个Entry本质上是一个单向链表 
 20     private Entry[] table; 
 21  
 22     // WeakHashMap的大小,它是WeakHashMap保存的键值对的数量 
 23     private int size; 
 24  
 25     // WeakHashMap的阈值,用于判断是否需要调整WeakHashMap的容量(threshold = 容量*加载因子) 
 26     private int threshold; 
 27  
 28     // 加载因子实际大小 
 29     private final float loadFactor; 
 30  
 31     // queue保存的是“已被GC清除”的“弱引用的键”。 
 32     // 弱引用和ReferenceQueue 是联合使用的:如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中 
 33     private final ReferenceQueue<K> queue = new ReferenceQueue<K>(); 
 34  
 35     // WeakHashMap被改变的次数 
 36     private volatile int modCount; 
 37  
 38     // 指定“容量大小”和“加载因子”的构造函数 
 39     public WeakHashMap(int initialCapacity, float loadFactor) { 
 40         if (initialCapacity < 0) 
 41             throw new IllegalArgumentException("Illegal Initial Capacity: "+ 
 42                                                initialCapacity); 
 43         // WeakHashMap的最大容量只能是MAXIMUM_CAPACITY 
 44         if (initialCapacity > MAXIMUM_CAPACITY) 
 45             initialCapacity = MAXIMUM_CAPACITY; 
 46  
 47         if (loadFactor <= 0 || Float.isNaN(loadFactor)) 
 48             throw new IllegalArgumentException("Illegal Load factor: "+ 
 49                                                loadFactor); 
 50         // 找出“大于initialCapacity”的最小的2的幂 
 51         int capacity = 1; 
 52         while (capacity < initialCapacity) 
 53             capacity <<= 1; 
 54         // 创建Entry数组,用来保存数据 
 55         table = new Entry[capacity]; 
 56         // 设置“加载因子” 
 57         this.loadFactor = loadFactor; 
 58         // 设置“WeakHashMap阈值”,当WeakHashMap中存储数据的数量达到threshold时,就需要将WeakHashMap的容量加倍。 
 59         threshold = (int)(capacity * loadFactor); 
 60     } 
 61  
 62     // 指定“容量大小”的构造函数 
 63     public WeakHashMap(int initialCapacity) { 
 64         this(initialCapacity, DEFAULT_LOAD_FACTOR); 
 65     } 
 66  
 67     // 默认构造函数。 
 68     public WeakHashMap() { 
 69         this.loadFactor = DEFAULT_LOAD_FACTOR; 
 70         threshold = (int)(DEFAULT_INITIAL_CAPACITY); 
 71         table = new Entry[DEFAULT_INITIAL_CAPACITY]; 
 72     } 
 73  
 74     // 包含“子Map”的构造函数 
 75     public WeakHashMap(Map<? extends K, ? extends V> m) { 
 76         this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, 16), 
 77              DEFAULT_LOAD_FACTOR); 
 78         // 将m中的全部元素逐个添加到WeakHashMap中 
 79         putAll(m); 
 80     } 
 81  
 82     // 键为null的mask值。 
 83     // 因为WeakReference中允许“null的key”,若直接插入“null的key”,将其当作弱引用时,会被删除。 
 84     // 因此,这里对于“key为null”的清空,都统一替换为“key为NULL_KEY”,“NULL_KEY”是“静态的final常量”。 
 85     private static final Object NULL_KEY = new Object(); 
 86  
 87     // 对“null的key”进行特殊处理 
 88     private static Object maskNull(Object key) { 
 89         return (key == null ? NULL_KEY : key); 
 90     } 
 91  
 92     // 还原对“null的key”的特殊处理 
 93     private static <K> K unmaskNull(Object key) { 
 94         return (K) (key == NULL_KEY ? null : key); 
 95     } 
 96  
 97     // 判断“x”和“y”是否相等 
 98     static boolean eq(Object x, Object y) { 
 99         return x == y || x.equals(y); 
100     } 
101  
102     // 返回索引值 
103     // h & (length-1)保证返回值的小于length 
104     static int indexFor(int h, int length) { 
105         return h & (length-1); 
106     } 
107  
108     // 清空table中无用键值对。原理如下: 
109     // (01) 当WeakHashMap中某个“弱引用的key”由于没有再被引用而被GC收回时, 
110     //   被回收的“该弱引用key”也被会被添加到"ReferenceQueue(queue)"中。 
111     // (02) 当我们执行expungeStaleEntries时, 
112     //   就遍历"ReferenceQueue(queue)"中的所有key 
113     //   然后就在“WeakReference的table”中删除与“ReferenceQueue(queue)中key”对应的键值对 
114     private void expungeStaleEntries() { 
115         Entry<K,V> e; 
116         while ( (e = (Entry<K,V>) queue.poll()) != null) { 
117             int h = e.hash; 
118             int i = indexFor(h, table.length); 
119  
120             Entry<K,V> prev = table[i]; 
121             Entry<K,V> p = prev; 
122             while (p != null) { 
123                 Entry<K,V> next = p.next; 
124                 if (p == e) { 
125                     if (prev == e) 
126                         table[i] = next; 
127                     else 
128                         prev.next = next; 
129                     e.next = null;  // Help GC 
130                     e.value = null; //  "   " 
131                     size--; 
132                     break; 
133                 } 
134                 prev = p; 
135                 p = next; 
136             } 
137         } 
138     } 
139  
140     // 获取WeakHashMap的table(存放键值对的数组) 
141     private Entry[] getTable() { 
142         // 删除table中“已被GC回收的key对应的键值对” 
143         expungeStaleEntries(); 
144         return table; 
145     } 
146  
147     // 获取WeakHashMap的实际大小 
148     public int size() { 
149         if (size == 0) 
150             return 0; 
151         // 删除table中“已被GC回收的key对应的键值对” 
152         expungeStaleEntries(); 
153         return size; 
154     } 
155  
156     public boolean isEmpty() { 
157         return size() == 0; 
158     } 
159  
160     // 获取key对应的value 
161     public V get(Object key) { 
162         Object k = maskNull(key); 
163         // 获取key的hash值。 
164         int h = HashMap.hash(k.hashCode()); 
165         Entry[] tab = getTable(); 
166         int index = indexFor(h, tab.length); 
167         Entry<K,V> e = tab[index]; 
168         // 在“该hash值对应的链表”上查找“键值等于key”的元素 
169         while (e != null) { 
170             if (e.hash == h && eq(k, e.get())) 
171                 return e.value; 
172             e = e.next; 
173         } 
174         return null; 
175     } 
176  
177     // WeakHashMap是否包含key 
178     public boolean containsKey(Object key) { 
179         return getEntry(key) != null; 
180     } 
181  
182     // 返回“键为key”的键值对 
183     Entry<K,V> getEntry(Object key) { 
184         Object k = maskNull(key); 
185         int h = HashMap.hash(k.hashCode()); 
186         Entry[] tab = getTable(); 
187         int index = indexFor(h, tab.length); 
188         Entry<K,V> e = tab[index]; 
189         while (e != null && !(e.hash == h && eq(k, e.get()))) 
190             e = e.next; 
191         return e; 
192     } 
193  
194     // 将“key-value”添加到WeakHashMap中 
195     public V put(K key, V value) { 
196         K k = (K) maskNull(key); 
197         int h = HashMap.hash(k.hashCode()); 
198         Entry[] tab = getTable(); 
199         int i = indexFor(h, tab.length); 
200  
201         for (Entry<K,V> e = tab[i]; e != null; e = e.next) { 
202             // 若“该key”对应的键值对已经存在,则用新的value取代旧的value。然后退出! 
203             if (h == e.hash && eq(k, e.get())) { 
204                 V oldValue = e.value; 
205                 if (value != oldValue) 
206                     e.value = value; 
207                 return oldValue; 
208             } 
209         } 
210  
211         // 若“该key”对应的键值对不存在于WeakHashMap中,则将“key-value”添加到table中 
212         modCount++; 
213         Entry<K,V> e = tab[i]; 
214         tab[i] = new Entry<K,V>(k, value, queue, h, e); 
215         if (++size >= threshold) 
216             resize(tab.length * 2); 
217         return null; 
218     } 
219  
220     // 重新调整WeakHashMap的大小,newCapacity是调整后的单位 
221     void resize(int newCapacity) { 
222         Entry[] oldTable = getTable(); 
223         int oldCapacity = oldTable.length; 
224         if (oldCapacity == MAXIMUM_CAPACITY) { 
225             threshold = Integer.MAX_VALUE; 
226             return; 
227         } 
228  
229         // 新建一个newTable,将“旧的table”的全部元素添加到“新的newTable”中, 
230         // 然后,将“新的newTable”赋值给“旧的table”。 
231         Entry[] newTable = new Entry[newCapacity]; 
232         transfer(oldTable, newTable); 
233         table = newTable; 
234  
235         if (size >= threshold / 2) { 
236             threshold = (int)(newCapacity * loadFactor); 
237         } else { 
238             // 删除table中“已被GC回收的key对应的键值对” 
239             expungeStaleEntries(); 
240             transfer(newTable, oldTable); 
241             table = oldTable; 
242         } 
243     } 
244  
245     // 将WeakHashMap中的全部元素都添加到newTable中 
246     private void transfer(Entry[] src, Entry[] dest) { 
247         for (int j = 0; j < src.length; ++j) { 
248             Entry<K,V> e = src[j]; 
249             src[j] = null; 
250             while (e != null) { 
251                 Entry<K,V> next = e.next; 
252                 Object key = e.get(); 
253                 if (key == null) { 
254                     e.next = null;  // Help GC 
255                     e.value = null; //  "   " 
256                     size--; 
257                 } else { 
258                     int i = indexFor(e.hash, dest.length); 
259                     e.next = dest[i]; 
260                     dest[i] = e; 
261                 } 
262                 e = next; 
263             } 
264         } 
265     } 
266  
267     // 将"m"的全部元素都添加到WeakHashMap中 
268     public void putAll(Map<? extends K, ? extends V> m) { 
269         int numKeysToBeAdded = m.size(); 
270         if (numKeysToBeAdded == 0) 
271             return; 
272  
273         // 计算容量是否足够, 
274         // 若“当前实际容量 < 需要的容量”,则将容量x2。 
275         if (numKeysToBeAdded > threshold) { 
276             int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1); 
277             if (targetCapacity > MAXIMUM_CAPACITY) 
278                 targetCapacity = MAXIMUM_CAPACITY; 
279             int newCapacity = table.length; 
280             while (newCapacity < targetCapacity) 
281                 newCapacity <<= 1; 
282             if (newCapacity > table.length) 
283                 resize(newCapacity); 
284         } 
285  
286         // 将“m”中的元素逐个添加到WeakHashMap中。 
287         for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) 
288             put(e.getKey(), e.getValue()); 
289     } 
290  
291     // 删除“键为key”元素 
292     public V remove(Object key) { 
293         Object k = maskNull(key); 
294         // 获取哈希值。 
295         int h = HashMap.hash(k.hashCode()); 
296         Entry[] tab = getTable(); 
297         int i = indexFor(h, tab.length); 
298         Entry<K,V> prev = tab[i]; 
299         Entry<K,V> e = prev; 
300  
301         // 删除链表中“键为key”的元素 
302         // 本质是“删除单向链表中的节点” 
303         while (e != null) { 
304             Entry<K,V> next = e.next; 
305             if (h == e.hash && eq(k, e.get())) { 
306                 modCount++; 
307                 size--; 
308                 if (prev == e) 
309                     tab[i] = next; 
310                 else 
311                     prev.next = next; 
312                 return e.value; 
313             } 
314             prev = e; 
315             e = next; 
316         } 
317  
318         return null; 
319     } 
320  
321     // 删除“键值对” 
322     Entry<K,V> removeMapping(Object o) { 
323         if (!(o instanceof Map.Entry)) 
324             return null; 
325         Entry[] tab = getTable(); 
326         Map.Entry entry = (Map.Entry)o; 
327         Object k = maskNull(entry.getKey()); 
328         int h = HashMap.hash(k.hashCode()); 
329         int i = indexFor(h, tab.length); 
330         Entry<K,V> prev = tab[i]; 
331         Entry<K,V> e = prev; 
332  
333         // 删除链表中的“键值对e” 
334         // 本质是“删除单向链表中的节点” 
335         while (e != null) { 
336             Entry<K,V> next = e.next; 
337             if (h == e.hash && e.equals(entry)) { 
338                 modCount++; 
339                 size--; 
340                 if (prev == e) 
341                     tab[i] = next; 
342                 else 
343                     prev.next = next; 
344                 return e; 
345             } 
346             prev = e; 
347             e = next; 
348         } 
349  
350         return null; 
351     } 
352  
353     // 清空WeakHashMap,将所有的元素设为null 
354     public void clear() { 
355         while (queue.poll() != null) 
356             ; 
357  
358         modCount++; 
359         Entry[] tab = table; 
360         for (int i = 0; i < tab.length; ++i) 
361             tab[i] = null; 
362         size = 0; 
363  
364         while (queue.poll() != null) 
365             ; 
366     } 
367  
368     // 是否包含“值为value”的元素 
369     public boolean containsValue(Object value) { 
370         // 若“value为null”,则调用containsNullValue()查找 
371         if (value==null) 
372             return containsNullValue(); 
373  
374         // 若“value不为null”,则查找WeakHashMap中是否有值为value的节点。 
375         Entry[] tab = getTable(); 
376         for (int i = tab.length ; i-- > 0 ;) 
377             for (Entry e = tab[i] ; e != null ; e = e.next) 
378                 if (value.equals(e.value)) 
379                     return true; 
380         return false; 
381     } 
382  
383     // 是否包含null值 
384     private boolean containsNullValue() { 
385         Entry[] tab = getTable(); 
386         for (int i = tab.length ; i-- > 0 ;) 
387             for (Entry e = tab[i] ; e != null ; e = e.next) 
388                 if (e.value==null) 
389                     return true; 
390         return false; 
391     } 
392  
393     // Entry是单向链表。 
394     // 它是 “WeakHashMap链式存储法”对应的链表。 
395     // 它实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数 
396     private static class Entry<K,V> extends WeakReference<K> implements Map.Entry<K,V> { 
397         private V value; 
398         private final int hash; 
399         // 指向下一个节点 
400         private Entry<K,V> next; 
401  
402         // 构造函数。 
403         Entry(K key, V value, 
404           ReferenceQueue<K> queue, 
405               int hash, Entry<K,V> next) { 
406             super(key, queue); 
407             this.value = value; 
408             this.hash  = hash; 
409             this.next  = next; 
410         } 
411  
412         public K getKey() { 
413             return WeakHashMap.<K>unmaskNull(get()); 
414         } 
415  
416         public V getValue() { 
417             return value; 
418         } 
419  
420         public V setValue(V newValue) { 
421         V oldValue = value; 
422             value = newValue; 
423             return oldValue; 
424         } 
425  
426         // 判断两个Entry是否相等 
427         // 若两个Entry的“key”和“value”都相等,则返回true。 
428         // 否则,返回false 
429         public boolean equals(Object o) { 
430             if (!(o instanceof Map.Entry)) 
431                 return false; 
432             Map.Entry e = (Map.Entry)o; 
433             Object k1 = getKey(); 
434             Object k2 = e.getKey(); 
435             if (k1 == k2 || (k1 != null && k1.equals(k2))) { 
436                 Object v1 = getValue(); 
437                 Object v2 = e.getValue(); 
438                 if (v1 == v2 || (v1 != null && v1.equals(v2))) 
439                     return true; 
440             } 
441             return false; 
442         } 
443  
444         // 实现hashCode() 
445         public int hashCode() { 
446             Object k = getKey(); 
447             Object v = getValue(); 
448             return  ((k==null ? 0 : k.hashCode()) ^ 
449                      (v==null ? 0 : v.hashCode())); 
450         } 
451  
452         public String toString() { 
453             return getKey() + "=" + getValue(); 
454         } 
455     } 
456  
457     // HashIterator是WeakHashMap迭代器的抽象出来的父类,实现了公共了函数。 
458     // 它包含“key迭代器(KeyIterator)”、“Value迭代器(ValueIterator)”和“Entry迭代器(EntryIterator)”3个子类。 
459     private abstract class HashIterator<T> implements Iterator<T> { 
460         // 当前索引 
461         int index; 
462         // 当前元素 
463         Entry<K,V> entry = null; 
464         // 上一次返回元素 
465         Entry<K,V> lastReturned = null; 
466         // expectedModCount用于实现fast-fail机制。 
467         int expectedModCount = modCount; 
468  
469         // 下一个键(强引用) 
470         Object nextKey = null; 
471  
472         // 当前键(强引用) 
473         Object currentKey = null; 
474  
475         // 构造函数 
476         HashIterator() { 
477             index = (size() != 0 ? table.length : 0); 
478         } 
479  
480         // 是否存在下一个元素 
481         public boolean hasNext() { 
482             Entry[] t = table; 
483  
484             // 一个Entry就是一个单向链表 
485             // 若该Entry的下一个节点不为空,就将next指向下一个节点; 
486             // 否则,将next指向下一个链表(也是下一个Entry)的不为null的节点。 
487             while (nextKey == null) { 
488                 Entry<K,V> e = entry; 
489                 int i = index; 
490                 while (e == null && i > 0) 
491                     e = t[--i]; 
492                 entry = e; 
493                 index = i; 
494                 if (e == null) { 
495                     currentKey = null; 
496                     return false; 
497                 } 
498                 nextKey = e.get(); // hold on to key in strong ref 
499                 if (nextKey == null) 
500                     entry = entry.next; 
501             } 
502             return true; 
503         } 
504  
505         // 获取下一个元素 
506         protected Entry<K,V> nextEntry() { 
507             if (modCount != expectedModCount) 
508                 throw new ConcurrentModificationException(); 
509             if (nextKey == null && !hasNext()) 
510                 throw new NoSuchElementException(); 
511  
512             lastReturned = entry; 
513             entry = entry.next; 
514             currentKey = nextKey; 
515             nextKey = null; 
516             return lastReturned; 
517         } 
518  
519         // 删除当前元素 
520         public void remove() { 
521             if (lastReturned == null) 
522                 throw new IllegalStateException(); 
523             if (modCount != expectedModCount) 
524                 throw new ConcurrentModificationException(); 
525  
526             WeakHashMap.this.remove(currentKey); 
527             expectedModCount = modCount; 
528             lastReturned = null; 
529             currentKey = null; 
530         } 
531  
532     } 
533  
534     // value的迭代器 
535     private class ValueIterator extends HashIterator<V> { 
536         public V next() { 
537             return nextEntry().value; 
538         } 
539     } 
540  
541     // key的迭代器 
542     private class KeyIterator extends HashIterator<K> { 
543         public K next() { 
544             return nextEntry().getKey(); 
545         } 
546     } 
547  
548     // Entry的迭代器 
549     private class EntryIterator extends HashIterator<Map.Entry<K,V>> { 
550         public Map.Entry<K,V> next() { 
551             return nextEntry(); 
552         } 
553     } 
554  
555     // WeakHashMap的Entry对应的集合 
556     private transient Set<Map.Entry<K,V>> entrySet = null; 
557  
558     // 返回“key的集合”,实际上返回一个“KeySet对象” 
559     public Set<K> keySet() { 
560         Set<K> ks = keySet; 
561         return (ks != null ? ks : (keySet = new KeySet())); 
562     } 
563  
564     // Key对应的集合 
565     // KeySet继承于AbstractSet,说明该集合中没有重复的Key。 
566     private class KeySet extends AbstractSet<K> { 
567         public Iterator<K> iterator() { 
568             return new KeyIterator(); 
569         } 
570  
571         public int size() { 
572             return WeakHashMap.this.size(); 
573         } 
574  
575         public boolean contains(Object o) { 
576             return containsKey(o); 
577         } 
578  
579         public boolean remove(Object o) { 
580             if (containsKey(o)) { 
581                 WeakHashMap.this.remove(o); 
582                 return true; 
583             } 
584             else 
585                 return false; 
586         } 
587  
588         public void clear() { 
589             WeakHashMap.this.clear(); 
590         } 
591     } 
592  
593     // 返回“value集合”,实际上返回的是一个Values对象 
594     public Collection<V> values() { 
595         Collection<V> vs = values; 
596         return (vs != null ?  vs : (values = new Values())); 
597     } 
598  
599     // “value集合” 
600     // Values继承于AbstractCollection,不同于“KeySet继承于AbstractSet”, 
601     // Values中的元素能够重复。因为不同的key可以指向相同的value。 
602     private class Values extends AbstractCollection<V> { 
603         public Iterator<V> iterator() { 
604             return new ValueIterator(); 
605         } 
606  
607         public int size() { 
608             return WeakHashMap.this.size(); 
609         } 
610  
611         public boolean contains(Object o) { 
612             return containsValue(o); 
613         } 
614  
615         public void clear() { 
616             WeakHashMap.this.clear(); 
617         } 
618     } 
619  
620     // 返回“WeakHashMap的Entry集合” 
621     // 它实际是返回一个EntrySet对象 
622     public Set<Map.Entry<K,V>> entrySet() { 
623         Set<Map.Entry<K,V>> es = entrySet; 
624         return es != null ? es : (entrySet = new EntrySet()); 
625     } 
626  
627     // EntrySet对应的集合 
628     // EntrySet继承于AbstractSet,说明该集合中没有重复的EntrySet。 
629     private class EntrySet extends AbstractSet<Map.Entry<K,V>> { 
630         public Iterator<Map.Entry<K,V>> iterator() { 
631             return new EntryIterator(); 
632         } 
633  
634         // 是否包含“值(o)” 
635         public boolean contains(Object o) { 
636             if (!(o instanceof Map.Entry)) 
637                 return false; 
638             Map.Entry e = (Map.Entry)o; 
639             Object k = e.getKey(); 
640             Entry candidate = getEntry(e.getKey()); 
641             return candidate != null && candidate.equals(e); 
642         } 
643  
644         // 删除“值(o)” 
645         public boolean remove(Object o) { 
646             return removeMapping(o) != null; 
647         } 
648  
649         // 返回WeakHashMap的大小 
650         public int size() { 
651             return WeakHashMap.this.size(); 
652         } 
653  
654         // 清空WeakHashMap 
655         public void clear() { 
656             WeakHashMap.this.clear(); 
657         } 
658  
659         // 拷贝函数。将WeakHashMap中的全部元素都拷贝到List中 
660         private List<Map.Entry<K,V>> deepCopy() { 
661             List<Map.Entry<K,V>> list = new ArrayList<Map.Entry<K,V>>(size()); 
662             for (Map.Entry<K,V> e : this) 
663                 list.add(new AbstractMap.SimpleEntry<K,V>(e)); 
664             return list; 
665         } 
666  
667         // 返回Entry对应的Object[]数组 
668         public Object[] toArray() { 
669             return deepCopy().toArray(); 
670         } 
671  
672         // 返回Entry对应的T[]数组(T[]我们新建数组时,定义的数组类型) 
673         public <T> T[] toArray(T[] a) { 
674             return deepCopy().toArray(a); 
675         } 
676     } 
677 }
WeakHashMap源码

说明:WeakHashMap和HashMap都是通过"拉链法"实现的散列表。它们的源码绝大部分内容都一样,这里就只是对它们不同的部分就是说明。

    WeakReference是“弱键”实现的哈希表。它这个“弱键”的目的就是:实现对“键值对”的动态回收。当“弱键”不再被使用到时,GC会回收它,WeakReference也会将“弱键”对应的键值对删除。
    “弱键”是一个“弱引用(WeakReference)”,在Java中,WeakReference和ReferenceQueue 是联合使用的。在WeakHashMap中亦是如此:如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。 接着,WeakHashMap会根据“引用队列”,来删除“WeakHashMap中已被GC回收的‘弱键’对应的键值对”。
    另外,理解上面思想的重点是通过 expungeStaleEntries() 函数去理解。

WeakHashMap遍历方式

4.1 遍历WeakHashMap的键值对

第一步:根据entrySet()获取WeakHashMap的“键值对”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。

 1 // 假设map是WeakHashMap对象 
 2 // map中的key是String类型,value是Integer类型 
 3 Integer integ = null; 
 4 Iterator iter = map.entrySet().iterator(); 
 5 while(iter.hasNext()) { 
 6     Map.Entry entry = (Map.Entry)iter.next(); 
 7     // 获取key 
 8     key = (String)entry.getKey(); 
 9         // 获取value 
10     integ = (Integer)entry.getValue(); 
11 }

4.2 遍历WeakHashMap的键

第一步:根据keySet()获取WeakHashMap的“键”的Set集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。

 1 // 假设map是WeakHashMap对象 
 2 // map中的key是String类型,value是Integer类型 
 3 String key = null; 
 4 Integer integ = null; 
 5 Iterator iter = map.keySet().iterator(); 
 6 while (iter.hasNext()) { 
 7         // 获取key 
 8     key = (String)iter.next(); 
 9         // 根据key,获取value 
10     integ = (Integer)map.get(key); 
11 }

4.3 遍历WeakHashMap的值

第一步:根据value()获取WeakHashMap的“值”的集合。
第二步:通过Iterator迭代器遍历“第一步”得到的集合。

1 // 假设map是WeakHashMap对象 
2 // map中的key是String类型,value是Integer类型 
3 Integer value = null; 
4 Collection c = map.values(); 
5 Iterator iter= c.iterator(); 
6 while (iter.hasNext()) { 
7     value = (Integer)iter.next(); 
8 }

WeakHashMap遍历测试程序如下

  1 import java.util.Map; 
  2 import java.util.Random; 
  3 import java.util.Iterator; 
  4 import java.util.WeakHashMap; 
  5 import java.util.HashSet; 
  6 import java.util.Map.Entry; 
  7 import java.util.Collection; 
  8  
  9 /* 
 10  * @desc 遍历WeakHashMap的测试程序。 
 11  *   (01) 通过entrySet()去遍历key、value,参考实现函数: 
 12  *        iteratorHashMapByEntryset() 
 13  *   (02) 通过keySet()去遍历key、value,参考实现函数: 
 14  *        iteratorHashMapByKeyset() 
 15  *   (03) 通过values()去遍历value,参考实现函数: 
 16  *        iteratorHashMapJustValues() 
 17  * 
 18  * @author skywang 
 19  */ 
 20 public class WeakHashMapIteratorTest { 
 21  
 22     public static void main(String[] args) { 
 23         int val = 0; 
 24         String key = null; 
 25         Integer value = null; 
 26         Random r = new Random(); 
 27         WeakHashMap map = new WeakHashMap(); 
 28  
 29         for (int i=0; i<12; i++) { 
 30             // 随机获取一个[0,100)之间的数字 
 31             val = r.nextInt(100); 
 32              
 33             key = String.valueOf(val); 
 34             value = r.nextInt(5); 
 35             // 添加到WeakHashMap中 
 36             map.put(key, value); 
 37             System.out.println(" key:"+key+" value:"+value); 
 38         } 
 39         // 通过entrySet()遍历WeakHashMap的key-value 
 40         iteratorHashMapByEntryset(map) ; 
 41          
 42         // 通过keySet()遍历WeakHashMap的key-value 
 43         iteratorHashMapByKeyset(map) ; 
 44          
 45         // 单单遍历WeakHashMap的value 
 46         iteratorHashMapJustValues(map);         
 47     } 
 48      
 49     /* 
 50      * 通过entry set遍历WeakHashMap 
 51      * 效率高! 
 52      */ 
 53     private static void iteratorHashMapByEntryset(WeakHashMap map) { 
 54         if (map == null) 
 55             return ; 
 56  
 57         System.out.println("\niterator WeakHashMap By entryset"); 
 58         String key = null; 
 59         Integer integ = null; 
 60         Iterator iter = map.entrySet().iterator(); 
 61         while(iter.hasNext()) { 
 62             Map.Entry entry = (Map.Entry)iter.next(); 
 63              
 64             key = (String)entry.getKey(); 
 65             integ = (Integer)entry.getValue(); 
 66             System.out.println(key+" -- "+integ.intValue()); 
 67         } 
 68     } 
 69  
 70     /* 
 71      * 通过keyset来遍历WeakHashMap 
 72      * 效率低! 
 73      */ 
 74     private static void iteratorHashMapByKeyset(WeakHashMap map) { 
 75         if (map == null) 
 76             return ; 
 77  
 78         System.out.println("\niterator WeakHashMap By keyset"); 
 79         String key = null; 
 80         Integer integ = null; 
 81         Iterator iter = map.keySet().iterator(); 
 82         while (iter.hasNext()) { 
 83             key = (String)iter.next(); 
 84             integ = (Integer)map.get(key); 
 85             System.out.println(key+" -- "+integ.intValue()); 
 86         } 
 87     } 
 88      
 89  
 90     /* 
 91      * 遍历WeakHashMap的values 
 92      */ 
 93     private static void iteratorHashMapJustValues(WeakHashMap map) { 
 94         if (map == null) 
 95             return ; 
 96          
 97         Collection c = map.values(); 
 98         Iterator iter= c.iterator(); 
 99         while (iter.hasNext()) { 
100             System.out.println(iter.next()); 
101        } 
102     } 
103 }

WeakHashMap示例

 1 import java.util.Iterator; 
 2 import java.util.Map; 
 3 import java.util.WeakHashMap; 
 4 import java.util.Date; 
 5 import java.lang.ref.WeakReference; 
 6  
 7 /** 
 8  * @desc WeakHashMap测试程序 
 9  * 
10  * @author skywang 
11  * @email kuiwu-wang@163.com 
12  */ 
13 public class WeakHashMapTest { 
14  
15     public static void main(String[] args) throws Exception { 
16         testWeakHashMapAPIs(); 
17     } 
18  
19     private static void testWeakHashMapAPIs() { 
20         // 初始化3个“弱键” 
21         String w1 = new String("one"); 
22         String w2 = new String("two"); 
23         String w3 = new String("three"); 
24         // 新建WeakHashMap 
25         Map wmap = new WeakHashMap(); 
26  
27         // 添加键值对 
28         wmap.put(w1, "w1"); 
29         wmap.put(w2, "w2"); 
30         wmap.put(w3, "w3"); 
31  
32         // 打印出wmap 
33         System.out.printf("\nwmap:%s\n",wmap ); 
34  
35         // containsKey(Object key) :是否包含键key 
36         System.out.printf("contains key two : %s\n",wmap.containsKey("two")); 
37         System.out.printf("contains key five : %s\n",wmap.containsKey("five")); 
38  
39         // containsValue(Object value) :是否包含值value 
40         System.out.printf("contains value 0 : %s\n",wmap.containsValue(new Integer(0))); 
41  
42         // remove(Object key) : 删除键key对应的键值对 
43         wmap.remove("three"); 
44  
45         System.out.printf("wmap: %s\n",wmap ); 
46  
47  
48  
49         // ---- 测试 WeakHashMap 的自动回收特性 ---- 
50      
51         // 将w1设置null。 
52         // 这意味着“弱键”w1再没有被其它对象引用,调用gc时会回收WeakHashMap中与“w1”对应的键值对 
53         w1 = null; 
54         // 内存回收。这里,会回收WeakHashMap中与“w1”对应的键值对 
55         System.gc(); 
56  
57         // 遍历WeakHashMap 
58         Iterator iter = wmap.entrySet().iterator(); 
59         while (iter.hasNext()) { 
60             Map.Entry en = (Map.Entry)iter.next(); 
61             System.out.printf("next : %s - %s\n",en.getKey(),en.getValue()); 
62         } 
63         // 打印WeakHashMap的实际大小 
64         System.out.printf(" after gc WeakHashMap size:%s\n", wmap.size()); 
65     } 
66 }
View Code

运行结果: 

1 wmap:{three=w3, one=w1, two=w2} 
2 contains key two : true 
3 contains key five : false 
4 contains value 0 : false 
5 wmap: {one=w1, two=w2} 
6 next : two - w2 
7  after gc WeakHashMap size:1


发布评论
IT序号网

微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!

ArrayList中ensureCapacity的使用与优化知识解答
你是第一个吃螃蟹的人
发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。