1 22 23 24 package com.mchange.v2.util; 25 26 import java.lang.ref.ReferenceQueue ; 27 import java.lang.ref.WeakReference ; 28 import java.util.AbstractSet ; 29 import java.util.ArrayList ; 30 import java.util.Collection ; 31 import java.util.HashMap ; 32 import java.util.HashSet ; 33 import java.util.Iterator ; 34 import java.util.Map ; 35 import java.util.Set ; 36 import java.util.Map.Entry; 37 38 39 import com.mchange.v1.util.AbstractMapEntry; 40 import com.mchange.v1.util.WrapperIterator; 41 42 46 54 public class DoubleWeakHashMap implements Map 55 { 56 HashMap inner; 57 ReferenceQueue keyQ = new ReferenceQueue (); 58 ReferenceQueue valQ = new ReferenceQueue (); 59 60 CheckKeyHolder holder = new CheckKeyHolder(); 61 62 Set userKeySet = null; 63 Collection valuesCollection = null; 64 65 public DoubleWeakHashMap() 66 { this.inner = new HashMap (); } 67 68 public DoubleWeakHashMap(int initialCapacity) 69 { this.inner = new HashMap ( initialCapacity ); } 70 71 public DoubleWeakHashMap(int initialCapacity, float loadFactor) 72 { this.inner = new HashMap ( initialCapacity, loadFactor ); } 73 74 public DoubleWeakHashMap(Map m) 75 { 76 this(); 77 putAll(m); 78 } 79 80 public void cleanCleared() 81 { 82 WKey wk; 83 while ((wk = (WKey) keyQ.poll()) != null) 84 inner.remove(wk); 85 86 WVal wv; 87 while ((wv = (WVal) valQ.poll()) != null) 88 inner.remove(wv.getWKey()); 89 } 90 91 public void clear() 92 { 93 cleanCleared(); 94 inner.clear(); 95 } 96 97 public boolean containsKey(Object key) 98 { 99 cleanCleared(); 100 try 101 { return inner.containsKey( holder.set(key) ); } 102 finally 103 { holder.clear(); } 104 } 105 106 public boolean containsValue(Object val) 107 { 108 for (Iterator ii = inner.values().iterator(); ii.hasNext();) 109 { 110 WVal wval = (WVal) ii.next(); 111 if (val.equals(wval.get())) 112 return true; 113 } 114 return false; 115 } 116 117 public Set entrySet() 118 { 119 cleanCleared(); 120 return new UserEntrySet(); 121 } 122 123 public Object get(Object key) 124 { 125 try 126 { 127 cleanCleared(); 128 WVal wval = (WVal) inner.get(holder.set(key)); 129 return (wval == null ? null : wval.get()); 130 } 131 finally 132 { holder.clear(); } 133 } 134 135 public boolean isEmpty() 136 { 137 cleanCleared(); 138 return inner.isEmpty(); 139 } 140 141 public Set keySet() 142 { 143 cleanCleared(); 144 if (userKeySet == null) 145 userKeySet = new UserKeySet(); 146 return userKeySet; 147 } 148 149 public Object put(Object key, Object val) 150 { 151 cleanCleared(); 152 WVal wout = doPut(key, val); 153 if (wout != null) 154 return wout.get(); 155 else 156 return null; 157 } 158 159 private WVal doPut(Object key, Object val) 160 { 161 WKey wk = new WKey(key, keyQ); 162 WVal wv = new WVal(wk, val, valQ); 163 return (WVal) inner.put(wk, wv); 164 } 165 166 public void putAll(Map m) 167 { 168 cleanCleared(); 169 for (Iterator ii = m.entrySet().iterator(); ii.hasNext();) 170 { 171 Map.Entry entry = (Map.Entry ) ii.next(); 172 this.doPut( entry.getKey(), entry.getValue() ); 173 } 174 } 175 176 public Object remove(Object key) 177 { 178 try 179 { 180 cleanCleared(); 181 WVal wv = (WVal) inner.remove( holder.set(key) ); 182 return (wv == null ? null : wv.get()); 183 } 184 finally 185 { holder.clear(); } 186 } 187 188 public int size() 189 { 190 cleanCleared(); 191 return inner.size(); 192 } 193 194 public Collection values() 195 { 196 if (valuesCollection == null) 197 this.valuesCollection = new ValuesCollection(); 198 return valuesCollection; 199 } 200 201 final static class CheckKeyHolder 202 { 203 Object checkKey; 204 205 public Object get() 206 {return checkKey; } 207 208 public CheckKeyHolder set(Object ck) 209 { 210 assert this.checkKey == null : "Illegal concurrenct use of DoubleWeakHashMap!"; 211 212 this.checkKey = ck; 213 return this; 214 } 215 216 public void clear() 217 { checkKey = null; } 218 219 public int hashCode() 220 { return checkKey.hashCode(); } 221 222 public boolean equals(Object o) 223 { 224 assert this.get() != null : "CheckedKeyHolder should never do an equality check while its value is null." ; 225 226 if (this == o) 227 return true; 228 else if (o instanceof CheckKeyHolder) 229 return this.get().equals( ((CheckKeyHolder) o).get() ); 230 else if (o instanceof WKey) 231 return this.get().equals( ((WKey) o).get() ); 232 else 233 return false; 234 } 235 } 236 237 final static class WKey extends WeakReference 238 { 239 int cachedHash; 240 241 WKey(Object keyObj, ReferenceQueue rq) 242 { 243 super(keyObj, rq); 244 this.cachedHash = keyObj.hashCode(); 245 } 246 247 public int hashCode() 248 { return cachedHash; } 249 250 public boolean equals(Object o) 251 { 252 if (this == o) 253 return true; 254 else if (o instanceof WKey) 255 { 256 WKey oo = (WKey) o; 257 Object myVal = this.get(); 258 Object ooVal = oo.get(); 259 if (myVal == null || ooVal == null) 260 return false; 261 else 262 return myVal.equals(ooVal); 263 } 264 else if (o instanceof CheckKeyHolder) 265 { 266 CheckKeyHolder oo = (CheckKeyHolder) o; 267 Object myVal = this.get(); 268 Object ooVal = oo.get(); 269 if (myVal == null || ooVal == null) 270 return false; 271 else 272 return myVal.equals(ooVal); 273 } 274 else 275 return false; 276 } 277 } 278 279 final static class WVal extends WeakReference 280 { 281 WKey key; 282 WVal(WKey key, Object valObj, ReferenceQueue rq) 283 { 284 super(valObj, rq); 285 this.key = key; 286 } 287 288 public WKey getWKey() 289 { return key; } 290 } 291 292 293 private final class UserEntrySet extends AbstractSet 294 { 295 private Set innerEntrySet() 296 { 297 cleanCleared(); 298 return inner.entrySet(); 299 } 300 301 public Iterator iterator() 302 { 303 return new WrapperIterator(innerEntrySet().iterator(), true) 304 { 305 protected Object transformObject(Object o) 306 { 307 Entry innerEntry = (Entry) o; 308 Object key = ((WKey) innerEntry.getKey()).get(); 309 Object val = ((WVal) innerEntry.getValue()).get(); 310 311 if (key == null || val == null) 312 return WrapperIterator.SKIP_TOKEN; 313 else 314 return new UserEntry( innerEntry, key, val ); 315 } 316 }; 317 } 318 319 public int size() 320 { return innerEntrySet().size(); } 321 } 322 323 class UserEntry extends AbstractMapEntry 324 { 325 Entry innerEntry; 326 Object key; 327 Object val; 328 329 UserEntry(Entry innerEntry, Object key, Object value) 330 { 331 this.innerEntry = innerEntry; 332 this.key = key; 333 this.val = val; 334 } 335 336 public final Object getKey() 337 { return key; } 338 339 public final Object getValue() 340 { return val; } 341 342 public final Object setValue(Object value) 343 { return innerEntry.setValue( new WVal( (WKey) innerEntry.getKey() ,value, valQ) ); } 344 } 345 346 class UserKeySet implements Set 347 { 348 public boolean add(Object o) 349 { 350 cleanCleared(); 351 throw new UnsupportedOperationException ("You cannot add to a Map's key set."); 352 } 353 354 public boolean addAll(Collection c) 355 { 356 cleanCleared(); 357 throw new UnsupportedOperationException ("You cannot add to a Map's key set."); 358 } 359 360 public void clear() 361 { DoubleWeakHashMap.this.clear(); } 362 363 public boolean contains(Object o) 364 { 365 return DoubleWeakHashMap.this.containsKey(o); 366 } 367 368 public boolean containsAll(Collection c) 369 { 370 for (Iterator ii = c.iterator(); ii.hasNext();) 371 if (! this.contains(ii.next())) 372 return false; 373 return true; 374 } 375 376 public boolean isEmpty() 377 { return DoubleWeakHashMap.this.isEmpty(); } 378 379 public Iterator iterator() 380 { 381 cleanCleared(); 382 return new WrapperIterator(DoubleWeakHashMap.this.inner.keySet().iterator(), true) 383 { 384 protected Object transformObject(Object o) 385 { 386 Object key = ((WKey) o).get(); 387 388 if (key == null) 389 return WrapperIterator.SKIP_TOKEN; 390 else 391 return key; 392 } 393 }; 394 } 395 396 public boolean remove(Object o) 397 { 398 return (DoubleWeakHashMap.this.remove(o) != null); 399 } 400 401 public boolean removeAll(Collection c) 402 { 403 boolean out = false; 404 for (Iterator ii = c.iterator(); ii.hasNext();) 405 out |= this.remove(ii.next()); 406 return out; 407 } 408 409 public boolean retainAll(Collection c) 410 { 411 boolean out = false; 413 for (Iterator ii = this.iterator(); ii.hasNext();) 414 { 415 if (!c.contains(ii.next())) 416 { 417 ii.remove(); 418 out = true; 419 } 420 } 421 return out; 422 } 423 424 public int size() 425 { return DoubleWeakHashMap.this.size(); } 426 427 public Object [] toArray() 428 { 429 cleanCleared(); 430 return new HashSet ( this ).toArray(); 431 } 432 433 public Object [] toArray(Object [] array) 434 { 435 cleanCleared(); 436 return new HashSet ( this ).toArray(array); 437 } 438 } 439 440 class ValuesCollection implements Collection 441 { 442 443 public boolean add(Object o) 444 { 445 cleanCleared(); 446 throw new UnsupportedOperationException ("DoubleWeakHashMap does not support adding to its values Collection."); 447 } 448 449 public boolean addAll(Collection c) 450 { 451 cleanCleared(); 452 throw new UnsupportedOperationException ("DoubleWeakHashMap does not support adding to its values Collection."); 453 } 454 455 public void clear() 456 { DoubleWeakHashMap.this.clear(); } 457 458 public boolean contains(Object o) 459 { return DoubleWeakHashMap.this.containsValue(o); } 460 461 public boolean containsAll(Collection c) 462 { 463 for (Iterator ii = c.iterator(); ii.hasNext();) 464 if (!this.contains(ii.next())) 465 return false; 466 return true; 467 } 468 469 public boolean isEmpty() 470 { return DoubleWeakHashMap.this.isEmpty(); } 471 472 public Iterator iterator() 473 { 474 return new WrapperIterator(inner.values().iterator(), true) 475 { 476 protected Object transformObject(Object o) 477 { 478 Object val = ((WVal) o).get(); 479 480 if (val == null) 481 return WrapperIterator.SKIP_TOKEN; 482 else 483 return val; 484 } 485 }; 486 } 487 488 public boolean remove(Object o) 489 { 490 cleanCleared(); 491 return removeValue(o); 492 } 493 494 public boolean removeAll(Collection c) 495 { 496 cleanCleared(); 497 boolean out = false; 498 for (Iterator ii = c.iterator(); ii.hasNext();) 499 out |= removeValue(ii.next()); 500 return out; 501 } 502 503 public boolean retainAll(Collection c) 504 { 505 cleanCleared(); 506 return retainValues(c); 507 } 508 509 public int size() 510 { return DoubleWeakHashMap.this.size(); } 511 512 public Object [] toArray() 513 { 514 cleanCleared(); 515 return new ArrayList (this).toArray(); 516 } 517 518 public Object [] toArray(Object [] array) 519 { 520 cleanCleared(); 521 return new ArrayList (this).toArray(array); 522 } 523 524 private boolean removeValue(Object val) 525 { 526 boolean out = false; 527 for (Iterator ii = inner.values().iterator(); ii.hasNext();) 528 { 529 WVal wv = (WVal) ii.next(); 530 if (val.equals(wv.get())) 531 { 532 ii.remove(); 533 out = true; 534 } 535 } 536 return out; 537 } 538 539 private boolean retainValues(Collection c) 540 { 541 boolean out = false; 542 for (Iterator ii = inner.values().iterator(); ii.hasNext();) 543 { 544 WVal wv = (WVal) ii.next(); 545 if (! c.contains(wv.get()) ) 546 { 547 ii.remove(); 548 out = true; 549 } 550 } 551 return out; 552 } 553 } 554 555 577 } 578 | Popular Tags |