View Javadoc

1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package org.apache.jdo.util;
18  
19  import java.lang.ref.ReferenceQueue;
20  import java.lang.ref.WeakReference;
21  import java.util.HashSet;
22  import java.util.Iterator;
23  
24  /***
25   * A weak HashSet. An element stored in the WeakHashSet might be
26   * garbage collected, if there is no strong reference to this element.
27   */
28  
29  public class WeakHashSet extends HashSet {
30      /***
31       * Helps to detect garbage collected values.
32       */
33      ReferenceQueue queue = new ReferenceQueue();
34  
35      /***
36       * Returns an iterator over the elements in this set.  The elements
37       * are returned in no particular order.
38       *
39       * @return an Iterator over the elements in this set.
40       */
41      public Iterator iterator() {
42          // remove garbage collected elements
43          processQueue();
44  
45          // get an iterator of the superclass WeakHashSet
46          final Iterator i = super.iterator();
47  
48          return new Iterator() {
49              public boolean hasNext() {
50                  return i.hasNext();
51              }
52  
53              public Object next() {
54                  // unwrap the element
55                  return getReferenceObject((WeakReference) i.next());
56              }
57  
58              public void remove() {
59                  // remove the element from the HashSet
60                  i.remove();
61              }
62          };
63      }
64  
65      /***
66       * Returns <code>true</code> if this set contains the specified element.
67       *
68       * @param o element whose presence in this set is to be tested.
69       * @return <code>true</code> if this set contains the specified element.
70       */
71      public boolean contains(Object o) {
72          return super.contains(WeakElement.create(o));
73      }
74  
75      /***
76       * Adds the specified element to this set if it is not already
77       * present.
78       *
79       * @param o element to be added to this set.
80       * @return <code>true</code> if the set did not already contain the specified
81       * element.
82       */
83      public boolean add(Object o) {
84          processQueue();
85          return super.add(WeakElement.create(o, this.queue));
86      }
87  
88      /***
89       * Removes the given element from this set if it is present.
90       *
91       * @param o object to be removed from this set, if present.
92       * @return <code>true</code> if the set contained the specified element.
93       */
94      public boolean remove(Object o) {
95          boolean ret = super.remove(WeakElement.create(o));
96          processQueue();
97          return ret;
98      }
99  
100     /***
101      * A convenience method to return the object held by the
102      * weak reference or <code>null</code> if it does not exist.
103      */
104     private final Object getReferenceObject(WeakReference ref) {
105         return (ref == null) ? null : ref.get();
106     }
107 
108     /***
109      * Removes all garbage collected values with their keys from the map.
110      * Since we don't know how much the ReferenceQueue.poll() operation
111      * costs, we should call it only in the add() method.
112      */
113     private final void processQueue() {
114         WeakElement wv = null;
115 
116         while ((wv = (WeakElement) this.queue.poll()) != null) {
117             super.remove(wv);
118         }
119     }
120 
121     /***
122      * A WeakHashSet stores objects of class WeakElement.
123      * A WeakElement wraps the element that should be stored in the WeakHashSet.
124      * WeakElement inherits from java.lang.ref.WeakReference.
125      * It redefines equals and hashCode which delegate to the corresponding methods
126      * of the wrapped element.
127      */
128     static private class WeakElement extends WeakReference {
129         private int hash;	/* Hashcode of key, stored here since the key
130                                may be tossed by the GC */
131 
132         private WeakElement(Object o) {
133             super(o);
134             hash = o.hashCode();
135         }
136 
137         private WeakElement(Object o, ReferenceQueue q) {
138             super(o, q);
139             hash = o.hashCode();
140         }
141 
142         private static WeakElement create(Object o) {
143             return (o == null) ? null : new WeakElement(o);
144         }
145 
146         private static WeakElement create(Object o, ReferenceQueue q) {
147             return (o == null) ? null : new WeakElement(o, q);
148         }
149 
150         /* A WeakElement is equal to another WeakElement iff they both refer to objects
151                that are, in turn, equal according to their own equals methods */
152         public boolean equals(Object o) {
153             if (this == o)
154                 return true;
155             if (!(o instanceof WeakElement))
156                 return false;
157             Object t = this.get();
158             Object u = ((WeakElement) o).get();
159             if (t == u) 
160                 return true;
161             if ((t == null) || (u == null))
162                 return false;
163             return t.equals(u);
164         }
165 
166         public int hashCode() {
167             return hash;
168         }
169     }
170 
171 }
172 
173