KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > registry > ReferenceHashSet


1 /*******************************************************************************
2  * Copyright (c) 2004, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.core.internal.registry;
12
13 import java.lang.ref.ReferenceQueue JavaDoc;
14 import java.lang.ref.SoftReference JavaDoc;
15 import java.lang.ref.WeakReference JavaDoc;
16
17 /**
18  * A hashset whose values can be garbage collected.
19  * This API is EXPERIMENTAL and provided as early access.
20  * @since 3.1
21  */

22 public class ReferenceHashSet {
23
24     private interface HashedReference {
25         int hashCode();
26         Object JavaDoc get();
27     }
28
29     private class HashableWeakReference extends WeakReference JavaDoc implements HashedReference {
30         public int hashCode;
31
32         public HashableWeakReference(Object JavaDoc referent, ReferenceQueue JavaDoc queue) {
33             super(referent, queue);
34             this.hashCode = referent.hashCode();
35         }
36
37         public boolean equals(Object JavaDoc obj) {
38             if (!(obj instanceof HashableWeakReference))
39                 return false;
40             Object JavaDoc referent = get();
41             Object JavaDoc other = ((HashableWeakReference) obj).get();
42             if (referent == null)
43                 return other == null;
44             return referent.equals(other);
45         }
46
47         public int hashCode() {
48             return this.hashCode;
49         }
50
51         public String JavaDoc toString() {
52             Object JavaDoc referent = get();
53             if (referent == null)
54                 return "[hashCode=" + this.hashCode + "] <referent was garbage collected>"; //$NON-NLS-1$ //$NON-NLS-2$
55
return "[hashCode=" + this.hashCode + "] " + referent.toString(); //$NON-NLS-1$ //$NON-NLS-2$
56
}
57     }
58
59     private class HashableSoftReference extends SoftReference JavaDoc implements HashedReference {
60         public int hashCode;
61
62         public HashableSoftReference(Object JavaDoc referent, ReferenceQueue JavaDoc queue) {
63             super(referent, queue);
64             this.hashCode = referent.hashCode();
65         }
66
67         public boolean equals(Object JavaDoc obj) {
68             if (!(obj instanceof HashableWeakReference))
69                 return false;
70             Object JavaDoc referent = get();
71             Object JavaDoc other = ((HashableWeakReference) obj).get();
72             if (referent == null)
73                 return other == null;
74             return referent.equals(other);
75         }
76
77         public int hashCode() {
78             return this.hashCode;
79         }
80
81         public String JavaDoc toString() {
82             Object JavaDoc referent = get();
83             if (referent == null)
84                 return "[hashCode=" + this.hashCode + "] <referent was garbage collected>"; //$NON-NLS-1$ //$NON-NLS-2$
85
return "[hashCode=" + this.hashCode + "] " + referent.toString(); //$NON-NLS-1$ //$NON-NLS-2$
86
}
87     }
88
89     private class StrongReference implements HashedReference {
90         private Object JavaDoc referent;
91
92         public StrongReference(Object JavaDoc referent, ReferenceQueue JavaDoc queue) {
93             this.referent = referent;
94         }
95
96         public int hashCode() {
97             return referent.hashCode();
98         }
99
100         public Object JavaDoc get() {
101             return referent;
102         }
103
104         public boolean equals(Object JavaDoc obj) {
105             return referent.equals(obj);
106         }
107     }
108
109     HashedReference[] values;
110
111     public int elementSize; // number of elements in the table
112

113     int threshold;
114
115     ReferenceQueue JavaDoc referenceQueue = new ReferenceQueue JavaDoc();
116
117     public ReferenceHashSet() {
118         this(5);
119     }
120
121     public ReferenceHashSet(int size) {
122         this.elementSize = 0;
123         this.threshold = size; // size represents the expected
124
// number of elements
125
int extraRoom = (int) (size * 1.75f);
126         if (this.threshold == extraRoom)
127             extraRoom++;
128         this.values = new HashedReference[extraRoom];
129     }
130
131     /**
132      * Constant indicating that hard references should be used.
133      */

134     final public static int HARD = 0;
135
136     /**
137      * Constant indiciating that soft references should be used.
138      */

139     final public static int SOFT = 1;
140
141     /**
142      * Constant indicating that weak references should be used.
143      */

144     final public static int WEAK = 2;
145
146     private HashedReference toReference(int type, Object JavaDoc referent) {
147         switch (type) {
148         case HARD:
149             return new StrongReference(referent, referenceQueue);
150         case SOFT:
151             return new HashableSoftReference(referent, referenceQueue);
152         case WEAK:
153             return new HashableWeakReference(referent, referenceQueue);
154         default:
155             throw new Error JavaDoc();
156         }
157     }
158
159     /*
160      * Adds the given object to this set. If an object that is equals to the
161      * given object already exists, do nothing. Returns the existing object or
162      * the new object if not found.
163      */

164     public Object JavaDoc add(Object JavaDoc obj, int referenceType) {
165         cleanupGarbageCollectedValues();
166         int index = (obj.hashCode() & 0x7FFFFFFF) % this.values.length;
167         HashedReference currentValue;
168         while ((currentValue = this.values[index]) != null) {
169             Object JavaDoc referent;
170             if (obj.equals(referent = currentValue.get())) {
171                 return referent;
172             }
173             index = (index + 1) % this.values.length;
174         }
175         this.values[index] = toReference(referenceType, obj);
176
177         // assumes the threshold is never equal to the size of the table
178
if (++this.elementSize > this.threshold)
179             rehash();
180
181         return obj;
182     }
183
184     private void addValue(HashedReference value) {
185         Object JavaDoc obj = value.get();
186         if (obj == null)
187             return;
188         int valuesLength = this.values.length;
189         int index = (value.hashCode() & 0x7FFFFFFF) % valuesLength;
190         HashedReference currentValue;
191         while ((currentValue = this.values[index]) != null) {
192             if (obj.equals(currentValue.get())) {
193                 return;
194             }
195             index = (index + 1) % valuesLength;
196         }
197         this.values[index] = value;
198
199         // assumes the threshold is never equal to the size of the table
200
if (++this.elementSize > this.threshold)
201             rehash();
202     }
203
204     private void cleanupGarbageCollectedValues() {
205         HashedReference toBeRemoved;
206         while ((toBeRemoved = (HashedReference) this.referenceQueue.poll()) != null) {
207             int hashCode = toBeRemoved.hashCode();
208             int valuesLength = this.values.length;
209             int index = (hashCode & 0x7FFFFFFF) % valuesLength;
210             HashedReference currentValue;
211             while ((currentValue = this.values[index]) != null) {
212                 if (currentValue == toBeRemoved) {
213                     // replace the value at index with the last value with the
214
// same hash
215
int sameHash = index;
216                     int current;
217                     while ((currentValue = this.values[current = (sameHash + 1) % valuesLength]) != null && currentValue.hashCode() == hashCode)
218                         sameHash = current;
219                     this.values[index] = this.values[sameHash];
220                     this.values[sameHash] = null;
221                     this.elementSize--;
222                     break;
223                 }
224                 index = (index + 1) % valuesLength;
225             }
226         }
227     }
228
229     public boolean contains(Object JavaDoc obj) {
230         return get(obj) != null;
231     }
232
233     /*
234      * Return the object that is in this set and that is equals to the given
235      * object. Return null if not found.
236      */

237     public Object JavaDoc get(Object JavaDoc obj) {
238         cleanupGarbageCollectedValues();
239         int valuesLength = this.values.length;
240         int index = (obj.hashCode() & 0x7FFFFFFF) % valuesLength;
241         HashedReference currentValue;
242         while ((currentValue = this.values[index]) != null) {
243             Object JavaDoc referent;
244             if (obj.equals(referent = currentValue.get())) {
245                 return referent;
246             }
247             index = (index + 1) % valuesLength;
248         }
249         return null;
250     }
251
252     private void rehash() {
253         ReferenceHashSet newHashSet = new ReferenceHashSet(this.elementSize * 2); // double the number of expected elements
254
newHashSet.referenceQueue = this.referenceQueue;
255         HashedReference currentValue;
256         for (int i = 0, length = this.values.length; i < length; i++)
257             if ((currentValue = this.values[i]) != null)
258                 newHashSet.addValue(currentValue);
259
260         this.values = newHashSet.values;
261         this.threshold = newHashSet.threshold;
262         this.elementSize = newHashSet.elementSize;
263     }
264
265     /*
266      * Removes the object that is in this set and that is equals to the given
267      * object. Return the object that was in the set, or null if not found.
268      */

269     public Object JavaDoc remove(Object JavaDoc obj) {
270         cleanupGarbageCollectedValues();
271         int valuesLength = this.values.length;
272         int index = (obj.hashCode() & 0x7FFFFFFF) % valuesLength;
273         HashedReference currentValue;
274         while ((currentValue = this.values[index]) != null) {
275             Object JavaDoc referent;
276             if (obj.equals(referent = currentValue.get())) {
277                 this.elementSize--;
278                 this.values[index] = null;
279                 rehash();
280                 return referent;
281             }
282             index = (index + 1) % valuesLength;
283         }
284         return null;
285     }
286
287     public int size() {
288         return this.elementSize;
289     }
290
291     public String JavaDoc toString() {
292         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc("{"); //$NON-NLS-1$
293
for (int i = 0, length = this.values.length; i < length; i++) {
294             HashedReference value = this.values[i];
295             if (value != null) {
296                 Object JavaDoc ref = value.get();
297                 if (ref != null) {
298                     buffer.append(ref.toString());
299                     buffer.append(", "); //$NON-NLS-1$
300
}
301             }
302         }
303         buffer.append("}"); //$NON-NLS-1$
304
return buffer.toString();
305     }
306
307     public Object JavaDoc[] toArray() {
308         cleanupGarbageCollectedValues();
309         Object JavaDoc[] result = new Object JavaDoc[elementSize];
310         int resultSize = 0;
311         for (int i = 0; i < values.length; i++) {
312             if (values[i] == null)
313                 continue;
314             Object JavaDoc tmp = values[i].get();
315             if (tmp != null)
316                 result[resultSize++] = tmp;
317         }
318         if (result.length == resultSize)
319             return result;
320         Object JavaDoc[] finalResult = new Object JavaDoc[resultSize];
321         System.arraycopy(result, 0, finalResult, 0, resultSize);
322         return finalResult;
323     }
324 }
325
Popular Tags