本章我们学习WeakHashMap。
我们首先对WeakHashMap有一个整体的了解,然后学习它的源码,最后通过示例学习使用WeakHashMap。 ? HashMap遍历方法
第五部分WeakHashMap示例
转载请注明出处:http://www.sychzs.cn/skywang12345/admin/EditPosts.aspx?postid=3311092
WeakHashMap简介
WeakHashMap 继承于 AbstractMap,并实现了 Map 接口。
和HashMap一样,WeakHashMap也是一个哈希表,并且键和值都可以为null。
但是WeakHashMap的键是一个“弱键”。在WeakHashMap中,当某个key不再正常使用时,它会自动从WeakHashMap中移除。更准确地说,对于给定的键,其映射的存在并不能阻止垃圾收集器丢弃该键,这使得该键可终结、终止,然后被回收。当一个键被终止时,它对应的键值对会被有效地从映射中删除。
这个“弱密钥”的原理是什么?粗略来说,是通过WeakReference和ReferenceQueue来实现的。 WeakHashMap的key是“weak key”,其类型为WeakReference; ReferenceQueue是一个队列,它会保存GC回收的“弱键”。实现步骤为:
(01) 创建一个新的WeakHashMap,并将“键值对”添加到WeakHashMap中。
其实WeakHashMap通过数组表来保存Entries(键值对);每个Entry实际上是一个单向链表,即Entry是一个键值对的链表。
(02) 当一个“弱键”不再被其他对象引用,且被GC回收时。当GC回收“弱键”时,“弱键”也会被添加到ReferenceQueue(队列)队列中。
(03)下次需要操作WeakHashMap时,我们会先同步表和队列。所有键值对都存储在表中,GC回收的键值对存储在队列中;为了同步它们,删除表中GC回收的键值对。
这就是“弱键”从 WeakHashMap 中自动删除的方式。
与HashMap一样,WeakHashMap也是不同步的。可以使用 Collections.synchronizedMap 方法构造同步 WeakHashMap。
WeakHashMap的构造函数
WeakHashMap 有 4 个构造函数,如下:
// 默认构造函数。 WeakHashMap() // 指定“容量大小”的构造函数 WeakHashMap(int容量) // 指定“容量大小”和“负载系数”的构造函数 WeakHashMap(int容量,浮动负载因子) // 包含“子Map”的构造函数 WeakHashMap(Map extends K, ? extends V> 地图)
WeakHashMap API
voidclear() 对象克隆() boolean containsKey(对象键) boolean containsValue(对象值) Set>entrySet() V get(对象键) 布尔值 isEmpty() 设置 keySet() V put(K键,V值)void putAll(地图 延伸 K, ? 延伸 V>地图) V 删除(对象键) int 尺寸() 收藏 值()
WeakHashMap的继承关系如下
java.lang.Object ↳ java.util.AbstractMap↳ java.util.WeakHashMap 公共类WeakHashMap 延伸抽象地图 实现地图 {}
WeakHashMap 与 Map 的关系如下:
从图中可以看出:
(01) WeakHashMap继承于AbstractMap,实现了Map接口。
(02) WeakHashMap 是一个哈希表,但它的键是“弱键”。 WeakHashMap 保护了几个重要的成员变量:table、size、threshold 、负载系数、modCount、队列。
table是一种Entry[]数组类型,Entry实际上是一个单向链表。哈希表的“键值对”存储在Entry数组中。
size是Hashtable的大小,即Hashtable保存的键值对的数量。
threshold是Hashtable的阈值,用于判断Hashtable的容量是否需要调整。阈值=“容量*负载系数”。
loadFactor 是负载系数。
modCount用于实现fail-fast机制
queue保存已“被GC清除”的“弱引用键”。
下面解释一下WeakHashMap
的源码1 包 java.util; 2 导入 java.lang.ref.WeakReference; 3 导入 java.lang.ref.ReferenceQueue; 4 5 公共 类WeakHashMap6 延伸 抽象地图 7 工具 地图 { 8 9 // 默认初始容量为 16,必须为 2 的幂。 10 私人 静态 257最终 intDEFAULT_INITIAL_CAPACITY = 16; 11 12 //最大容量(必须是2的幂且小于2的30次方,如果传入的容量太大,将以此值替换) 13 私人 静态 257最终 int 最大容量 = 1 << 30; 14 15 // 默认负载系数 16 私人 静态 257最终浮动DEFAULT_LOAD_FACTOR = 0.75f; 17 18 // 存储数据的入口数组,长度为2的幂。 19 // WeakHashMap 使用拉链方式实现。每个Entry本质上都是一个单向链表 20 私有 Entry[] 表; 21 22 // WeakHashMap 的大小,即 WeakHashMap 保存的键值对数量 23 私人 int 尺寸; 24 25 // WeakHashMap 的阈值,用于判断 WeakHashMap 的容量是否需要调整(阈值 = 容量 * 加载因子) 26 私有 int 阈值; 27 28 // 负载系数 实际尺寸 29 私人 最终 浮动负载系数; 30 31 // 队列存储已“被GC清除”的“弱引用键”。 32 // 弱引用和ReferenceQueue一起使用:如果弱引用引用的对象被垃圾回收,Java虚拟机会将弱引用添加到关联的对象中。在参考队列中 33 私有 最终参考队列 队列 = newReferenceQueue 静态对象maskNull(对象键){ 89返回(键==null? NULL_KEY :键); 90 } 91 92 // 恢复“空键”特殊处理 93 私人 静态(); 34 35 // WeakHashMap 被更改的次数 36 私人 易失性 int modCount; 37 38 // 指定“容量大小”和“负载系数”的构造函数 39 public WeakHashMap(int初始容量,浮动负载系数) { 40 if(初始容量 < 0) 41 抛出 新 IllegalArgumentException ("非法初始容量:“+ 42 初始容量); 43 // WeakHashMap 的最大容量只能是 MAXIMUM_CAPACITY 44 if(初始容量 > MAXIMUM_CAPACITY) 45 初始容量 = MAXIMUM_CAPACITY; 46 47 if (loadFactor <= 0 || Float.isNaN(loadFactor)) 48 抛出 new IllegalArgumentException ("非法负载系数:“+ 49 负载系数); 50 // 找到“大于initialCapacity”的最小2的幂 51 int 容量 = 1; 52 同时(容量<初始容量) 53容量 <<= 1; 54 // 创建Entry数组来保存数据 55 表 = 新 条目[容量]; 56 // 设置“负载系数” 57 这个.loadFactor = loadFactor; 58 // 设置“WeakHashMap 阈值”。当WeakHashMap存储的数据量达到阈值时,需要将WeakHashMap的容量增加一倍。 59阈值 = (int)(容量*负载系数); 60 } 61 62 // 指定“容量大小”的构造函数 63 public WeakHashMap(intinitialCapac实体){ 64 这个(初始容量,DEFAULT_LOAD_FACTOR); 65 } 66 67 // 默认构造函数。 68 public WeakHashMap() { 69这个.loadFactor =DEFAULT_LOAD_FACTOR; 70阈值 = (int)(DEFAULT_INITIAL_CAPACITY); 71 表 = 新 条目 [DEFAULT_INITIAL_CAPACITY]; 72 } 73 74 // 包含“子Map”的构造函数 75 public WeakHashMap(Map 扩展 K, ? 延伸 V> m) { 76 这个(Math.max((int) (m .size() / DEFAULT_LOAD_FACTOR) + 1, 16), 77DEFAULT_LOAD_FACTOR); 78 //将m中所有元素一一添加到WeakHashMap中 79 putAll(m); 80 } 81 82 // key 为空的掩码值。 83 // 因为WeakReference中允许“空键”,所以如果直接插入“空键”并视为弱引用,则会将其删除。 84 // 因此,“key is null”的清除统一替换为“key is NULL_KEY”,“NULL_KEY”是一个“静态最终常量”。 85 私人 静态 final 对象 NULL_KEY = new Object(); 86 87 //“空键”特殊处理 88privatestatic K unmaskNull(对象键) { 94 返回 (K) (key == NULL_KEY ? null:键); 95 } 96 97 // 判断“x”和“y”是否相等 98 静态 boolean eq(对象 x, 对象y) { 99 返回 x == y || x.equals(y); 100 } 101 102 // 返回索引值 103 // h & (length-1) 保证返回值小于length 104 静态 int 索引For(int h, int 长度) { 105 返回 h &(长度-1); 106 } 107 108 // 清除表中无用的键值对。原理如下: 109 // (01) 当 WeakHashMap 中的某个“弱引用键”因为不再被引用而被 GC 回收时, 110 // 回收的“弱引用key”也会被添加到“ReferenceQueue(队列)”中。 111 // (02) 当我们执行 expungeStaleEntries 时,112 // 遍历“ReferenceQueue(queue)”中的所有key 113 // 然后删除“弱引用表”中“key in ReferenceQueue(queue)”对应的键值对 114私有voidexpungeStaleEntries(){ 115条目 e; 116 while ((e = (Entry 190 e = e.下一个; 191 返回 e; 192 } 193 194 // 将“键值”添加到 WeakHashMap 195 public V put(K key, V value) {196 K k = (K) maskNull(key); 197 int h = HashMap.hash(k.hashCode()); 198 Entry[] 选项卡 = getTable(); 199 int i =indexFor(h, tab.length); 200 201 for(条目)queue.poll()) != null) { 117 int h = e.hash; 118 int i =indexFor(h, table.length); 119 120条目 上一个=表[i]; 121条目 p =上一页; 122 同时 (p != null) { 123条目 下一个=www.sychzs.cn; 124 if (p == e) { 125 if(上一页 == e) 126表[i]=下一个; 127 其他 128 上一个.下一个 = 下一个;129 www.sychzs.cn = null; //帮助GC 130 e.value = null; //“” 131尺寸--; 132打破; 133 } 134 上一页 = p; 135 p = 接下来; 136} 137 } 138} 139 140 // 获取WeakHashMap的表(存储键值对的数组) 141 private Entry[] getTable() { 142 // 删除表中“已被GC回收的key对应的键值对” 143 expungeStaleEntries(); 144返回表; 145 } 146 147 // 获取WeakHashMap的实际大小 148公共int尺寸(){ 149 if(尺寸 == 0) 150 返回 0; 151 // 删除表中“已被GC回收的key对应的键值对”152 expungeStaleEntries(); 153返回尺寸; 154 } 155 156 public boolean isEmpty() { 157 返回 size() == 0; 158 } 159 160 // 获取key对应的值 161 public V get(对象键) { 162 对象 k = maskNull(key); 163 // 获取key的哈希值。 164 int h = HashMap.hash(k.hashCode()); 165 Entry[] 选项卡 = getTable(); 166 int索引=indexFor(h, tab.length); 167条目 e = tab[索引]; 168 // 在“哈希值对应的链表”上找到“key值等于key”的元素 169 同时 (e != null) { 170 if (e.hash == h && eq(k, e.get())) 171 返回 e.值; 172 e = e.下一个; 173 }174返回null; 175 } 176 177 // WeakHashMap 是否包含 key 178 public boolean containsKey(对象键) { 179 return getEntry(key) != null; 180 } 181 182 // 返回键值对“key is key” 183 Entry getEntry(对象键) { 184 对象 k = maskNull(key); 185 int h = HashMap.hash(k.hashCode()); 186 Entry[] 选项卡 = getTable(); 187int索引=indexFor(h, tab.length); 188条目 e = tab[索引]; 189 同时 (e != null && !(e.hash == h && eq(k, e.get()))) e = tab[i]; e != null; e = www.sychzs.cn) { 202 // 如果“this key”对应的键值对已存在,则将旧值替换为新值。然后退出! 203 if (h == e.hash && eq(k, e.get())) { 204 V 旧值 = e.值; 205 if(值!= 旧值) 206 e.值=值; 207 返回旧值; 208 } 209 } 210 211 // 如果 WeakHashMap 中不存在“该键”对应的键值对,则将该“键值”添加到表中 212 modCount++; 213条目 e = tab[i];214 tab[i] = 新条目 (k, 值, 队列, h, e); 215 if(++大小 >= 阈值) 216 调整大小(制表符长度 * 2); 217返回null; 218 } 219 220 // 调整WeakHashMap大小,newCapacity为调整后的单位 221 void 调整大小(int newCapacity){ 222 Entry[] oldTable = getTable(); 223 intoldCapacity =oldTable.length; 224 if(旧容量 == MAXIMUM_CAPACITY){ 225阈值= Integer.MAX_VALUE; 226 返回; 227 } 228 229 // 创建一个新的newTable,并将“旧表”的所有元素添加到“新newTable”中。 230 // 然后,将“新newTable”分配给“旧表”。 231 Entry[] newTable = new Entry[newCapacity]; 232 传输(旧表,新表);233桌子=新桌子; 234 235 if(大小 >= 阈值 / 2){ 236阈值 = (int)(新容量*负载系数); 237 } 其他 { 238 // 删除表中“已被GC回收的key对应的键值对” 239 expungeStaleEntries(); 240 传输(新表,旧表); 241桌子=旧桌子; 242 } 243 } 244 245 //将WeakHashMap中的所有元素添加到newTable中 246 private void 传输(Entry[] src,Entry[] dest){ 247 for (int j = 0; j < src.length; ++j) { 248条目 e = src[j]; 249 src[j] = null; 250 同时 (e != null) { 251条目 下一个 = e.下一个;252 对象键 = e.get(); 253 if(键==null){ 254 www.sychzs.cn = null; //帮助GC 255 e.value = null; //“” 256尺寸--; 257 } 其他 { 258 int i =indexFor(e.hash, dest.length); 259 www.sychzs.cn = dest[i]; 260 dest[i] = e; 261 } 262 e = 接下来; 263 } 264 } 265 } 266 267 //将“m”的全部元素都添加到WeakHashMap中 268 public void putAll(Map extends K, ? 延伸 V> m) { 269 int numKeysToBeAdded = m.size();270 if(numKeysToBeAdded == 0) 271 返回; 272 273 // 计算能力是否足够? 274 // 如果“当前实际容量 < 所需容量”,则增加容量 x2。 275 if(numKeysToBeAdded > 阈值){ 276 int 目标容量 = (int)(numKeysToBeAdded / loadFactor + 1 ); 277 if(目标容量> MAXIMUM_CAPACITY) 278目标容量=MAXIMUM_CAPACITY; 279 int newCapacity = table.length; 280 while(新容量 < 目标容量) 281 新容量 <<= 1; 282 if(新容量 > 表.length) 283 调整大小(新容量); 284 } 285 286 // 将“m”中的元素一一添加到WeakHashMap中。 287 for(Map.Entry 延伸 K, ? 延伸 V> e : m.entrySet()) 288 put(e.getKey(), e.getValue()); 289 } 290 291 // 删除“key is key”元素 292 public V 删除(对象键){ 293 对象 k = maskNull(key); 294 // 获取哈希值。 295 int h = HashMap.hash(k.hashCode()); 296 Entry[] 选项卡 = getTable(); 297 int i =indexFor(h, tab.length); 298 条目 上一个 = tab[i]; 299 条目 e = 上一页; 300 301 // 删除链表中带有“key”的元素 302 // 本质是“删除单向链表中的节点” 303 同时 (e != null) { 304条目 下一个=e.下一个; 305 if (h == e.hash && eq(k, e.get())) {306 modCount++; 307尺寸--; 308 if(上一页 == e) 309 tab[i] = 下一个; 310 其他 311 上一个.下一个 = 下一个; 312 返回 e.值; 313 } 314 前 = e; 315 e = 接下来; 316 } 317 318返回null; 319 } 320 321 // 删除“键值对” 322条目 removeMapping(Object o) { 323 if (!(o 实例地图.条目)) 324返回null; 325 Entry[] 选项卡 = getTable(); 326Map.Entry条目=(Map.Entry)o; 327 对象 k = maskNull(entry.getKey()); 328 int h = HashMap.hash(k.hashCode());329 int i =indexFor(h, tab.length); 330 条目 上一个 = tab[i]; 331 条目 e = 上一页; 332 333 // 删除链表中的“键值对e” 334 // 本质是“删除单向链表中的节点” 335 同时 (e != null) { 336条目 下一个=e.下一个; 337 if (h == e.hash && e.equals(entry)) { 338 modCount++; 339尺寸--; 340 if(上一页 == e) 341 tab[i] = 下一个; 342 其他 343 上一个.下一个 = 下一个; 344 返回 e; 345 } 346 前 = e; 347 e = 接下来; 348 } 349 350返回null; 351 } 352353 // 清除WeakHashMap并将所有元素设置为null 354 public voidclear() { 355 while (queue.poll() != null) 356; 357 358 modCount++; 359 Entry[] 选项卡 = 表; 对于 (INT I = 0; I i) 361 tab[i] = null; 362尺寸=0; 363 364 while (queue.poll() != null) 365; 366 } 367 368 //是否包含“值就是值”元素 369 public boolean containsValue(对象值){ 370 // 如果“value 为 null”,则调用 containsNullValue() 查找 371 if(值==null) 372 返回 containsNullValue(); 373 374 // 如果“value不为空”,则查找WeakHashMap中是否存在有value的节点。 375 Entry[] 选项卡 = getTable(); 376 for (int i = tab.length ; i-- > 0;) 377 for(条目 e = tab[i] ; e != null ; e = e.下一个) 378 if (value.equals(e.value)) 379返回真; 380返回假; 381 } 382 383 // 是否包含null值 384 私有 boolean containsNullValue() { 385 Entry[] 选项卡 = getTable(); 386 for (int i = tab.length ; i-- > 0;) 387 for(条目 e = tab[i] ; e != null ; e = e.下一个) 388 if(e.value==null) 389返回真; 390返回假; 391 } 392393 // 条目是一个单向链表。 394 // 是“WeakHashMap链式存储方式”对应的链表。 395 // 实现了Map.Entry接口,即实现了getKey()、getValue()、setValue(V value)、equals(Object o)、hashCode()函数 396私有静态类 条目扩展弱引用 439返回真; 440 } 441返回假; 442 } 443 444 // 实现hashCode() 445 public int hashCode() { 446 对象 k = getKey(); 447 对象 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 // 包含3个子类:“键迭代器(KeyIterator)”、“值迭代器(ValueIterator)”和“条目迭代器(EntryIterator)”。 459私有抽象类 HashIterator实现 地图。进入 { 397 私有V值; 398私人最终int 哈希; 399 //指向下一个节点 400 私人进入 436 对象 v1 = getValue(); 437 对象 v2 = e.getValue(); 438 if (v1 == v2 || (v1 != null && v1.equals(v2)))下一个; 401 402 // 构造函数。 403 输入(K键、V值、 404ReferenceQueue 队列, 405 int哈希,Entry 下一个){ 406 super(键,队列); 407 这个.value =值; 408 这个.hash =哈希;409 这个.下一个=下一个; 410 } 411 412 public K getKey() { 413 returnWeakHashMap. unmaskNull(get()); 414 } 415 416 public V getValue() { 417 返回值; 418 } 419 420 public V setValue(V newValue) { 421V旧值=值; 422值=新值; 423 返回旧值; 424 } 425 426 //判断两个条目是否适合 427 //若两个条目的“key”和“value”都满足,则返回true。 428 //否则,返回false 429 public boolean 等于(对象 o){ 430 if (!(o 实例地图.条目)) 431返回假;432 Map.Entry e = (Map.Entry)o; 433 对象 k1 = getKey(); 434 对象 k2 = e.getKey(); 435 if (k1 == k2 || (k1 != null && k1.equals(k2))) { 实现迭代器 KeySet())); 562 } 563 564 // 按键对应设置 565 //KeySet继承自AbstractSet,表示集合中没有重复的Key。 566 私有 类密钥集扩展 摘要集{ 460 // 当前索引 461 int 索引; 462 // 当前元素 463条目 条目=null; 464 // 最后返回的元素 465条目 最后返回=null; 466 // ExpectedModCount 用于实现快速失败机制。 467 int预期ModCount = modCount; 468 469 // 下一个键(强引用) 470 对象 nextKey = null; 471 472 // 当前键(强参考) 473 对象 currentKey = null; 474 475 // 构造函数 476 HashIterator() { 477索引 = (size() != 0 ? table.length : 0); 478 } 479 480 // 是否有下一个元素 481 public boolean hasNext() { 482 Entry[] t = 表; 483 484 // 条目是一个单向链表 485 // 如果Entry的下一个节点不为空,则指向next到下一个节点; 486 // 否则,指向下一个链表(也是下一个Entry)中不为空的节点。 487 while (nextKey == null) { 488条目 e =条目;489 int i = 索引; 490 同时 (e == null && i > 0) 491 e = t[--i]; 492条目= e; 493指数 = i; 494 if(e == null){ 495当前Key = null; 496返回假; 497 } 498 nextKey = e.get(); // 按住强参考键 499 if(nextKey == null) 500条目=条目.下一个; 501 } 502返回真; 503 } 504 505 // 获取下一个元素 506 受保护条目 nextEntry() { 507 if (modCount !=预期ModCount)508 抛出 new ConcurrentModificationException(); 509 if (nextKey == null && !有下一个()) 510 抛出 new NoSuchElementException(); 511 512 最后返回= 条目; 513条目=条目.下一个; 514 currentKey = nextKey; 515 nextKey = null; 516 返回最后返回; 517 } 518 519 // 当前删除元素 520 公共 void 删除(){ 521 if(最后返回 == null) 522 抛出 new IllegalStateException(); 523 if (modCount !=预期ModCount) 524 抛出 new ConcurrentModificationException(); 525526WeakHashMap.这个.remove(currentKey); 527预期ModCount = modCount; 528最后返回=空; 529当前Key = null; 530 } 531 532 } 533 534 //迭代器的值 535 私有 类 ValueIterator 扩展 HashIterator { 536 公共 V next() { 537 返回 nextEntry().value; 538} 539 } 540 541 //关键的迭代器 542 私有 类 KeyIterator 扩展 HashIterator { 543 公共 K next() { 550 返回 nextEntry().getKey(); 545 } 546 } 547 548 //入口迭代器549 私有 类 EntryIterator 扩展 HashIterator > { 550 publicMap.Entry next() { 551 返回 nextEntry(); 552 } 553 } 554 555 //WeakHashMap的Entry对应的集合 556 private 瞬态 Set > EntrySet = 空; 557 558 //返回一个“密钥集合”,实际上返回一个“KeySet对象” 559 public 设置 keySet() { 560设置 ks =按键集; 561 返回 (ks != null ? ks : (keySet = new {567 public迭代器 迭代器(){ 568 返回 new KeyIterator(); 569 } 570 571 公共int尺寸(){ 572 returnWeakHashMap.this.size(); 573} 574 575 public boolean 包含(对象 o){ 576 返回 containsKey(o); 577} 578 579 public boolean 删除(对象o){ 580 if(包含Key(o)){ 581WeakHashMap.这个.remove(o); 582返回真; 583 } 584 其他 585返回假; 586 } 587 588 public voidclear() {589WeakHashMap.这个.clear(); 590 } 591 } 592 593 //返回“值集合”,实际上返回的是一个Values对象 594 公共集合 值(){ 595集合 vs =值; 596 返回(vs!= null ? vs :(值= 新值())); 597 } 598 599 //“超值收藏” 600 // Values继承自AbstractCollection,与“KeySet继承自AbstractSet”不同。 601 // Values中的元素可以重复。因为不同的键可以指向同一个值。 602 私有 类值扩展 抽象集合 { 603 公共迭代器 迭代器(){ 604 返回 new ValueIterator(); 605 } 606 607公共int尺寸(){608 returnWeakHashMap.this.size(); 609 } 610 611 public boolean 包含(对象 o){ 612 返回 containsValue(o); 613} 614 615 public voidclear() { 616WeakHashMap.这个.clear(); 617 } 618} 619 620 // 返回“WeakHashMap 的条目集合” 621 // 它实际上返回一个 EntrySet 对象 622 public Set > EntrySet() { 623Set > es =entrySet; 624返回es!=null? es : (entrySet = new EntrySet()); 625} 626 627 // EntrySet 对应的集合 628 // EntrySet继承于AbstractSet,表示这个集合中没有重复的EntrySet。 629 私有 类 EntrySet 扩展 AbstractSet > { 630 public 迭代器 662 for(地图条目> iterator() { 631 返回 new EntryIterator(); 632 } 633 634 // 是否包含“值(o)” 635 public boolean 包含(对象 o){ 636 if (!(o 实例地图.条目)) 637返回假; 638 Map.Entry e = (Map.Entry)o; 639 对象 k = e.getKey(); 640 候选条目= getEntry(e.getKey()); 641 返回候选!= null &&候选.等于(e); 642 } 643 644 // 删除“值(o)” 645 public boolean 删除(对象o){646 returnremoveMapping(o) != null; 647 } 648 649 // 返回WeakHashMap的大小 650 公共int尺寸(){ 651 return WeakHashMap.this.size(); 652 } 653 654 // 清除WeakHashMap 655 公共 voidclear() { 656 WeakHashMap.这个.clear(); 657 } 658 659 //复印功能。将WeakHashMap中的所有元素复制到List 660 private List > deepCopy() { 661列表 >列表=新ArrayList > (尺寸()); e : 这个) 663 list.add(new AbstractMap.SimpleEntry (e)); 664 返回列表;665 } 666 667 // 返回Entry 对应的Object[]数组 668 public Object[] toArray() { 669 返回 deepCopy().toArray(); 670 } 671 672 // 返回Entry对应的T[]数组(T[]是我们创建新数组时定义的数组类型) 673 public T[] toArray(T[] a) { 674 返回 deepCopy().toArray(a); 675 } 676 } 677 }
说明:WeakHashMap 和 HashMap 都是通过“拉链法”实现的哈希表。它们的大部分源代码都是相同的,这里只是对它们不同部分的解释。
WeakReference 是一个用“弱键”实现的哈希表。这个“弱键”的目的就是实现“键值对”的动态回收。当“弱键”不再使用时,GC会回收它,WeakReference也会删除“弱键”对应的键值对。
“弱键”就是“弱引用(WeakReference)”。在Java中,WeakReference和ReferenceQueue是联合使用的。 WeakHashMap 中也是如此:如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会将该弱引用添加到与其关联的引用队列中。然后,WeakHashMap会根据“引用队列”删除“WeakHashMap中已被GC回收的‘弱键’对应的键值对”。
另外,理解上述思想的关键是通过expungeStaleEntries()函数来理解。
4.1 遍历WeakHashMap的键值对
第一步:根据entrySet()获取WeakHashMap的“键值对”Set集合。
第二步:通过Iterator遍历“第一步”得到的集合。
// 假设map是一个WeakHashMap对象 //map中的key为String类型,value为Integer类型 整数 integ = null; 迭代器 iter = map.entrySet().iterator(); while(iter.hasNext()) { Map.Entry条目= (Map.Entry)www.sychzs.cn(); //获取钥匙 key = (String)entry.getKey(); //获取价值integ = (整数)entry.getValue(); }
4.2 遍历WeakHashMap的key
第一步:根据keySet()获取WeakHashMap的“key”的Set集合。
第二步:通过Iterator遍历“第一步”得到的集合。
// 假设map是一个WeakHashMap对象 //map中的key为String类型,value为Integer类型 字符串键 = null; 整数 = null; 迭代器 iter = map.keySet().iterator(); while (iter.hasNext()) { //获取钥匙 key = (String)www.sychzs.cn(); //根据key获取值 integ = (整数)map.get(key); }
4.3 遍历WeakHashMap的值
第一步:根据value()获取WeakHashMap“值”的集合。
第二步:通过Iterator遍历“第一步”得到的集合。
// 假设map是一个WeakHashMap对象 //map中的key为String类型,value为Integer类型 整数值 = null; 集合c =map.values(); 迭代器 iter= c.iterator();while (iter.hasNext()) { value = (整数)www.sychzs.cn(); }
WeakHashMap遍历测试程序如下:
1 导入 www.sychzs.cn; 2 导入 java.util.Random; 3 导入 java.util.Iterator; 4 导入 java.util.WeakHashMap; 5 导入 java.util.HashSet; 6 导入 java.util.Map.Entry; 7 导入 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 公共 类 WeakHashMapIteratorT东部时间{ 21 22 公共 静态 void main(String[] args) { 23 int val = 0; 24 字符串键 = null; 25 整数值 = null; 26 随机 r = new 随机(); 27 WeakHashMap 映射 = new WeakHashMap(); 28 29 对于 (int i=0; i< 12; i++) { 30 // 随机获取 [0,100) 之间的数字 31 val = r.nextInt(100); 32 33 key = String.valueOf(val); 34值 = 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的值即可 46 iteratorHashMapJustValues(map); 47 } 48 49 /* 50 * 通过条目集遍历 WeakHashMap 51 * 效率高! 52 */ 53 私人 静态 void iteratorHashMapByEntryset(WeakHashMap map) { 54 if(地图 == null) 55 返回 ; 56 57 System.out.println("\niterator WeakHashMap 按条目集"); 58 字符串键 = null; 59 整数 integ = null; 60 迭代器 iter = map.entrySet().iterator(); 61 while(iter.hasNext()) { 62 Map.Entry 条目 = (Map.Entry)www.sychzs.cn(); 63 64 key = (String)entry.getKey(); 65 整数= (Integer)entry.getValue(); 66 System.out.println(key+" -- "+integ.intValue()); 67 } 68 } 69 70 /* 71 * 通过keyset遍历WeakHashMap 72 * 效率低下! 73 */ 74 私人 静态 void iteratorHashMapByKeyset(WeakHashMap map) { 75 if(地图==null) 76 返回 ; 77 78 System.out.println("\niterator WeakHashMap 按键集"); 79 字符串键 = null; 80 整数 integ = null; 81 迭代器 iter = map.keySet().iterator(); 82 while (iter.hasNext()) { 83 key = (String)www.sychzs.cn(); 84 integ = (整数)map.get(key); 85 System.out.println(key+" -- "+integ.intValue()); 86 } 87 } 88 89 90 /* 91 * 遍历WeakHashMap的值 92 */ 93 私人 静态 void iteratorHashMapJustValues(WeakHashMap map) { 94 if(地图==null) 95 返回 ; 96 97 集合 c = map.values(); 98 迭代器 iter= c.iterator(); 99 while (iter.hasNext()) { 100 System.out.println(www.sychzs.cn()); 101 } 102 } 103 }
下面通过实例来学习如何使用WeakHashMap
1 导入 java.util.Iterator; 2 导入 www.sychzs.cn; 3 导入 java.util.WeakHashMap; 4 导入 www.sychzs.cn; 5 导入 java.lang.ref.WeakReference; 6 7 /** 8 * @desc WeakHashMap 测试程序 9 * 10 * @author skywang 11 * @邮箱 support@www.sychzs.cn 12 */ 13 public 类 WeakHashMapTest { 14 15 公共 静态 void main(String[] args) 抛出 异常 { 16 testWeakHashMapAPIs(); 17 } 18 19私有静态void testWeakHashMapAPIs() {20 // 初始化3个“弱键” 21字符串w1 = new字符串(“一”); 22字符串w2 = new字符串(“两个”); 23 String w3 = new String("三"); 24 //新WeakHashMap 25 映射 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("包含密钥二:%s\n",wmap.containsKey("二")); 37 System.out.printf("包含密钥五: %s\n",wmap.containsKey("五")); 38 39 // containsValue(Object value):是否包含value值40 System.out.printf("包含值 0 : %s\n",wmap.containsValue(new Integer(0))); 41 42 //remove(Object key):删除键key对应的键值对 43 wmap.remove("三"); 44 45 System.out.printf("wmap: %s\n",wmap ); 46 47 48 49 // ---- 测试WeakHashMap的自动回收功能---- 50 51 // 将 w1 设置为空。 52 // 这意味着“弱键”w1不再被其他对象引用。调用gc时,WeakHashMap中“w1”对应的键值对将会被回收 53 w1 = 空; 54 // 内存回收。这里WeakHashMap中“w1”对应的键值对会被回收 55 System.gc(); 56 57 //遍历WeakHashMap 58 迭代器 iter = wmap.entrySet().iterator(); 59 while (iter.hasNext()) { 60 Map.Entry en = (Map.Entry)www.sychzs.cn();61 System.out.printf("下一个: %s - %s\n",en.getKey(),en.getValue()); 62} 63 // 打印WeakHashMap的实际大小 64 System.out.printf(" gc后WeakHashMap大小:%s\n", wmap.size()); 65 } 66 }
运行结果:
wmap:{三=w3,一=w1,二=w2} 包含关键二:true 包含关键五个:false 包含值 0 : false wmap: {一个=w1, 两个=w2} 下一个 : 两个 - w2 gc后WeakHashMap大小:1