KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mchange > v2 > util > DoubleWeakHashMap


1 /*
2  * Distributed as part of c3p0 v.0.9.1
3  *
4  * Copyright (C) 2005 Machinery For Change, Inc.
5  *
6  * Author: Steve Waldman <swaldman@mchange.com>
7  *
8  * This library is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License version 2.1, as
10  * published by the Free Software Foundation.
11  *
12  * This software is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this software; see the file LICENSE. If not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */

22
23
24 package com.mchange.v2.util;
25
26 import java.lang.ref.ReferenceQueue JavaDoc;
27 import java.lang.ref.WeakReference JavaDoc;
28 import java.util.AbstractSet JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Collection JavaDoc;
31 import java.util.HashMap JavaDoc;
32 import java.util.HashSet JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.Map JavaDoc;
35 import java.util.Set JavaDoc;
36 import java.util.Map.Entry;
37
38
39 import com.mchange.v1.util.AbstractMapEntry;
40 import com.mchange.v1.util.WrapperIterator;
41
42 //TODO -- ensure that cleanCleared() gets called only once, even in methods implemented
43
// as loops. (cleanCleared() is idempotent, so the repeated calls are okay,
44
// but they're wasteful.
45

46 /**
47  * <p>This class is <u>not</u> Thread safe.
48  * Use in single threaded contexts, or contexts where
49  * single threaded-access can be guaranteed, or
50  * wrap with Collections.synchronizedMap().</p>
51  *
52  * <p>This class does not accept null keys or values.</p>
53  */

54 public class DoubleWeakHashMap implements Map JavaDoc
55 {
56     HashMap JavaDoc inner;
57     ReferenceQueue JavaDoc keyQ = new ReferenceQueue JavaDoc();
58     ReferenceQueue JavaDoc valQ = new ReferenceQueue JavaDoc();
59     
60     CheckKeyHolder holder = new CheckKeyHolder();
61     
62     Set JavaDoc userKeySet = null;
63     Collection JavaDoc valuesCollection = null;
64     
65     public DoubleWeakHashMap()
66     { this.inner = new HashMap JavaDoc(); }
67     
68     public DoubleWeakHashMap(int initialCapacity)
69     { this.inner = new HashMap JavaDoc( initialCapacity ); }
70     
71     public DoubleWeakHashMap(int initialCapacity, float loadFactor)
72     { this.inner = new HashMap JavaDoc( initialCapacity, loadFactor ); }
73     
74     public DoubleWeakHashMap(Map JavaDoc 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 JavaDoc 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 JavaDoc val)
107     {
108         for (Iterator JavaDoc 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 JavaDoc entrySet()
118     {
119         cleanCleared();
120         return new UserEntrySet();
121     }
122
123     public Object JavaDoc get(Object JavaDoc 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 JavaDoc keySet()
142     {
143         cleanCleared();
144         if (userKeySet == null)
145             userKeySet = new UserKeySet();
146         return userKeySet;
147     }
148
149     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc 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 JavaDoc key, Object JavaDoc 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 JavaDoc m)
167     {
168        cleanCleared();
169        for (Iterator JavaDoc ii = m.entrySet().iterator(); ii.hasNext();)
170        {
171            Map.Entry JavaDoc entry = (Map.Entry JavaDoc) ii.next();
172            this.doPut( entry.getKey(), entry.getValue() );
173        }
174     }
175
176     public Object JavaDoc remove(Object JavaDoc 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 JavaDoc values()
195     {
196         if (valuesCollection == null)
197             this.valuesCollection = new ValuesCollection();
198         return valuesCollection;
199     }
200     
201     final static class CheckKeyHolder
202     {
203         Object JavaDoc checkKey;
204         
205         public Object JavaDoc get()
206         {return checkKey; }
207         
208         public CheckKeyHolder set(Object JavaDoc 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 JavaDoc 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 JavaDoc
238     {
239         int cachedHash;
240         
241         WKey(Object JavaDoc keyObj, ReferenceQueue JavaDoc 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 JavaDoc o)
251         {
252             if (this == o)
253                 return true;
254             else if (o instanceof WKey)
255             {
256                 WKey oo = (WKey) o;
257                 Object JavaDoc myVal = this.get();
258                 Object JavaDoc 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 JavaDoc myVal = this.get();
268                 Object JavaDoc 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 JavaDoc
280     {
281         WKey key;
282         WVal(WKey key, Object JavaDoc valObj, ReferenceQueue JavaDoc 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 JavaDoc
294     {
295         private Set JavaDoc innerEntrySet()
296         {
297             cleanCleared();
298             return inner.entrySet();
299         }
300
301         public Iterator JavaDoc iterator()
302         {
303             return new WrapperIterator(innerEntrySet().iterator(), true)
304             {
305                 protected Object JavaDoc transformObject(Object JavaDoc o)
306                 {
307                     Entry innerEntry = (Entry) o;
308                     Object JavaDoc key = ((WKey) innerEntry.getKey()).get();
309                     Object JavaDoc 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 JavaDoc key;
327         Object JavaDoc val;
328
329         UserEntry(Entry innerEntry, Object JavaDoc key, Object JavaDoc value)
330         {
331             this.innerEntry = innerEntry;
332             this.key = key;
333             this.val = val;
334         }
335
336         public final Object JavaDoc getKey()
337         { return key; }
338
339         public final Object JavaDoc getValue()
340         { return val; }
341
342         public final Object JavaDoc setValue(Object JavaDoc value)
343         { return innerEntry.setValue( new WVal( (WKey) innerEntry.getKey() ,value, valQ) ); }
344     }
345     
346     class UserKeySet implements Set JavaDoc
347     {
348         public boolean add(Object JavaDoc o)
349         {
350             cleanCleared();
351             throw new UnsupportedOperationException JavaDoc("You cannot add to a Map's key set.");
352         }
353
354         public boolean addAll(Collection JavaDoc c)
355         {
356             cleanCleared();
357             throw new UnsupportedOperationException JavaDoc("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 JavaDoc o)
364         {
365             return DoubleWeakHashMap.this.containsKey(o);
366         }
367
368         public boolean containsAll(Collection JavaDoc c)
369         {
370             for (Iterator JavaDoc 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 JavaDoc iterator()
380         {
381             cleanCleared();
382             return new WrapperIterator(DoubleWeakHashMap.this.inner.keySet().iterator(), true)
383             {
384                 protected Object JavaDoc transformObject(Object JavaDoc o)
385                 {
386                     Object JavaDoc 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 JavaDoc o)
397         {
398             return (DoubleWeakHashMap.this.remove(o) != null);
399         }
400
401         public boolean removeAll(Collection JavaDoc c)
402         {
403             boolean out = false;
404             for (Iterator JavaDoc ii = c.iterator(); ii.hasNext();)
405                 out |= this.remove(ii.next());
406             return out;
407         }
408
409         public boolean retainAll(Collection JavaDoc c)
410         {
411             //we implicitly cleanCleared() by calling iterator()
412
boolean out = false;
413             for (Iterator JavaDoc 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 JavaDoc[] toArray()
428         {
429             cleanCleared();
430             return new HashSet JavaDoc( this ).toArray();
431         }
432
433         public Object JavaDoc[] toArray(Object JavaDoc[] array)
434         {
435             cleanCleared();
436             return new HashSet JavaDoc( this ).toArray(array);
437         }
438     }
439
440     class ValuesCollection implements Collection JavaDoc
441     {
442
443         public boolean add(Object JavaDoc o)
444         {
445             cleanCleared();
446             throw new UnsupportedOperationException JavaDoc("DoubleWeakHashMap does not support adding to its values Collection.");
447         }
448
449         public boolean addAll(Collection JavaDoc c)
450         {
451             cleanCleared();
452             throw new UnsupportedOperationException JavaDoc("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 JavaDoc o)
459         { return DoubleWeakHashMap.this.containsValue(o); }
460
461         public boolean containsAll(Collection JavaDoc c)
462         {
463             for (Iterator JavaDoc 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 JavaDoc iterator()
473         {
474             return new WrapperIterator(inner.values().iterator(), true)
475             {
476                 protected Object JavaDoc transformObject(Object JavaDoc o)
477                 {
478                     Object JavaDoc 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 JavaDoc o)
489         {
490             cleanCleared();
491             return removeValue(o);
492         }
493
494         public boolean removeAll(Collection JavaDoc c)
495         {
496             cleanCleared();
497             boolean out = false;
498             for (Iterator JavaDoc ii = c.iterator(); ii.hasNext();)
499                 out |= removeValue(ii.next());
500             return out;
501         }
502
503         public boolean retainAll(Collection JavaDoc c)
504         {
505             cleanCleared();
506             return retainValues(c);
507         }
508
509         public int size()
510         { return DoubleWeakHashMap.this.size(); }
511
512         public Object JavaDoc[] toArray()
513         {
514             cleanCleared();
515             return new ArrayList JavaDoc(this).toArray();
516         }
517
518         public Object JavaDoc[] toArray(Object JavaDoc[] array)
519         {
520             cleanCleared();
521             return new ArrayList JavaDoc(this).toArray(array);
522         }
523
524         private boolean removeValue(Object JavaDoc val)
525         {
526             boolean out = false;
527             for (Iterator JavaDoc 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 JavaDoc c)
540         {
541             boolean out = false;
542             for (Iterator JavaDoc 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     /*
556     public static void main(String[] argv)
557     {
558         DoubleWeakHashMap m = new DoubleWeakHashMap();
559         //Set keySet = new HashSet();
560         //Set valSet = new HashSet();
561
562         while (true)
563         {
564             System.err.println( m.inner.size() );
565
566             //if (Math.random() < 0.1f)
567             // valSet.clear();
568             
569             Object key = new Object();
570             Object val = new long[100000];
571             //keySet.add(key);
572             //valSet.add(val);
573             m.put( key, val );
574         }
575     }
576     */

577 }
578
Popular Tags