KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jruby > util > WeakIdentityHashMap


1 /***** BEGIN LICENSE BLOCK *****
2  * Version: CPL 1.0/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Common Public
5  * License Version 1.0 (the "License"); you may not use this file
6  * except in compliance with the License. You may obtain a copy of
7  * the License at http://www.eclipse.org/legal/cpl-v10.html
8  *
9  * Software distributed under the License is distributed on an "AS
10  * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
11  * implied. See the License for the specific language governing
12  * rights and limitations under the License.
13  *
14  * Copyright (C) 2006 Kresten Krab Thorup <krab@gnu.org>
15  *
16  * Alternatively, the contents of this file may be used under the terms of
17  * either of the GNU General Public License Version 2 or later (the "GPL"),
18  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
19  * in which case the provisions of the GPL or the LGPL are applicable instead
20  * of those above. If you wish to allow use of your version of this file only
21  * under the terms of either the GPL or the LGPL, and not to allow others to
22  * use your version of this file under the terms of the CPL, indicate your
23  * decision by deleting the provisions above and replace them with the notice
24  * and other provisions required by the GPL or the LGPL. If you do not delete
25  * the provisions above, a recipient may use your version of this file under
26  * the terms of any one of the CPL, the GPL or the LGPL.
27  ***** END LICENSE BLOCK *****/

28
29 package org.jruby.util;
30
31 import java.lang.ref.ReferenceQueue JavaDoc;
32 import java.lang.ref.WeakReference JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.Map JavaDoc;
35 import java.util.NoSuchElementException JavaDoc;
36
37 /**
38  * Class <code>WeakIdentityHashMap</code> is a hash map that hashes
39  * objects based on System.identityHashMap, and holds weakly onto the
40  * key. This fails if values make reference to the keys!
41  *
42  * @author <a HREF="mailto:krab@trifork.com">Kresten Krab Thorup </a>
43  * @version 2.0
44  * @since 1.0
45  */

46 public class WeakIdentityHashMap extends GenericMap implements Map JavaDoc {
47
48     private static final float DEFAULT_RATIO = 0.75F;
49
50     private static final Object JavaDoc NULL_KEY = new Object JavaDoc();
51
52     private ReferenceQueue JavaDoc queue = new ReferenceQueue JavaDoc();
53
54     private static Object JavaDoc unmaskKey(Object JavaDoc key) {
55         if (key == NULL_KEY) {
56             return null;
57         } else {
58             return key;
59         }
60     }
61
62     private Object JavaDoc maskKey(Object JavaDoc key) {
63         if (key == null) {
64             return NULL_KEY;
65         } else {
66             return key;
67         }
68     }
69
70     class Entry extends WeakReference JavaDoc implements Map.Entry JavaDoc {
71         Entry next;
72
73         int key_hash;
74
75         Object JavaDoc value;
76
77         public int hashCode() {
78             return key_hash ^ valueHash(getValue());
79         }
80
81         public boolean equals(Object JavaDoc other) {
82             if (other instanceof Map.Entry JavaDoc) {
83                 Map.Entry JavaDoc ent = (Map.Entry JavaDoc) other;
84                 return getKey() == ent.getKey()
85                         && valueEquals(getValue(), ent.getValue());
86             } else {
87                 return false;
88             }
89         }
90
91         Entry(int key_hash, Object JavaDoc masked_key, Object JavaDoc value, Entry next, ReferenceQueue JavaDoc q) {
92             super(masked_key, q);
93             this.key_hash = key_hash;
94             this.value = value;
95             this.next = next;
96         }
97
98         Object JavaDoc getMaskedKey() {
99             return super.get();
100         }
101         
102         public Object JavaDoc getKey() {
103             return unmaskKey(getMaskedKey());
104         }
105
106         public Object JavaDoc getValue() {
107             return value;
108         }
109
110         public Object JavaDoc setValue(Object JavaDoc value) {
111             Object JavaDoc result = this.value;
112             this.value = value;
113             return result;
114         }
115
116         boolean sameKey(int hash, Object JavaDoc masked_key) {
117             return getMaskedKey() == masked_key;
118         }
119     }
120
121     /** the hash index */
122     private Entry[] table;
123
124     /** the current range for table. */
125     private int range;
126
127     private float ratio;
128
129     /** translate hash code bucket to index */
130     private int index(int hash) {
131         return (hash & 0x7ffffff) % range;
132     }
133
134     /** the default and only constructor */
135     public WeakIdentityHashMap() {
136         clear();
137     }
138
139     public WeakIdentityHashMap(int size) {
140         clear(Math.max(3, Math.round(size/DEFAULT_RATIO)));
141     }
142
143     public void clear() {
144         clear(3);
145     }
146     
147     void clear(int size) {
148         range = size;
149         this.size = 0;
150         ratio = DEFAULT_RATIO;
151         table = new Entry[range];
152     }
153
154     private void expunge() {
155         Entry e;
156         while ((e = (Entry) queue.poll()) != null) {
157             removeEntry(e);
158         }
159     }
160
161     /** return the element with the given key */
162     public Object JavaDoc get(Object JavaDoc key) {
163         Object JavaDoc masked_key = maskKey(key);
164         int hash = keyHash(masked_key);
165         return get(hash, masked_key);
166     }
167
168     private Object JavaDoc get(int hash, Object JavaDoc masked_key) {
169         int idx = index(hash);
170         expunge();
171         for (Entry ent = table[idx]; ent != null; ent = ent.next) {
172             if (ent.sameKey(hash, masked_key))
173                 return ent.value;
174         }
175
176         return null;
177     }
178
179     /** return the element with the given key */
180     public boolean containsKey(Object JavaDoc key) {
181         Object JavaDoc masked_key = maskKey(key);
182         int hash = keyHash(masked_key);
183         return containsKey(hash, masked_key);
184     }
185
186     private boolean containsKey(int hash, Object JavaDoc masked_key) {
187         int idx = index(hash);
188         expunge();
189         for (Entry ent = table[idx]; ent != null; ent = ent.next) {
190             if (ent.sameKey(hash, masked_key))
191                 return true;
192         }
193
194         return false;
195     }
196
197     public Object JavaDoc put(Object JavaDoc key, Object JavaDoc value) {
198         Object JavaDoc masked_key = maskKey(key);
199         int hash = keyHash(masked_key);
200         return put(hash, masked_key, value);
201     }
202
203     private Object JavaDoc put(int hash, Object JavaDoc masked_key, Object JavaDoc value) {
204         int idx = index(hash);
205
206         for (Entry ent = table[idx]; ent != null; ent = ent.next) {
207             if (ent.sameKey(hash, masked_key)) {
208                 return ent.setValue(value);
209             }
210         }
211
212         expunge();
213
214         if (1.0F * size / range > ratio) {
215             grow();
216             idx = index(hash);
217         }
218
219         table[idx] = new Entry(hash, masked_key, value, table[idx], queue);
220
221         size += 1;
222
223         return null;
224     }
225
226     public Object JavaDoc remove(Object JavaDoc key) {
227         key = maskKey(key);
228         int hash = keyHash(key);
229         return remove(hash, key);
230     }
231
232     public Object JavaDoc remove(int hash, Object JavaDoc key) {
233         key = maskKey(key);
234         int idx = index(hash);
235
236         Entry entry = table[idx];
237         if (entry != null) {
238
239             if (entry.sameKey(hash, key)) {
240                 table[idx] = entry.next;
241                 size -= 1;
242                 return entry.getValue();
243
244             } else {
245                 Entry ahead = entry.next;
246
247                 while (ahead != null) {
248                     if (ahead.sameKey(hash, key)) {
249                         entry.next = ahead.next;
250                         size -= 1;
251                         return ahead.getValue();
252                     }
253
254                     entry = ahead;
255                     ahead = ahead.next;
256                 }
257             }
258         }
259
260         // it was not found at all!
261
return null;
262     }
263
264     private void removeEntry(Entry ent) {
265         int idx = index(ent.key_hash);
266
267         Entry entry = table[idx];
268         if (entry != null) {
269
270             if (entry == ent) {
271                 table[idx] = entry.next;
272                 size -= 1;
273                 return;
274
275             } else {
276                 Entry ahead = entry.next;
277
278                 while (ahead != null) {
279                     if (ahead == ent) {
280                         entry.next = ahead.next;
281                         size -= 1;
282                         return;
283                     }
284
285                     entry = ahead;
286                     ahead = ahead.next;
287                 }
288             }
289         }
290         
291         valueRemoved(ent.value);
292     }
293
294     // can be overridden to be informed when objects are removed
295
protected void valueRemoved(Object JavaDoc value) {
296     }
297
298     private void grow() {
299         int old_range = range;
300         Entry[] old_table = table;
301
302         range = old_range * 2 + 1;
303         table = new Entry[range];
304
305         for (int i = 0; i < old_range; i++) {
306             Entry entry = old_table[i];
307
308             while (entry != null) {
309                 Entry ahead = entry.next;
310                 int idx = index(entry.key_hash);
311                 entry.next = table[idx];
312                 table[idx] = entry;
313                 entry = ahead;
314             }
315         }
316     }
317
318     final class EntryIterator implements Iterator JavaDoc {
319         int idx;
320
321         Entry entry;
322
323         EntryIterator() {
324             idx = 0;
325             expunge();
326             entry = table[0];
327             locateNext();
328         }
329
330         private void locateNext() {
331             // we reached the end of a list
332
while (entry == null) {
333                 // goto next bucket
334
idx += 1;
335                 if (idx == range) {
336                     // we reached the end
337
return;
338                 }
339
340                 // entry is the first element of this bucket
341
entry = table[idx];
342             }
343         }
344
345         public boolean hasNext() {
346             return (entry != null);
347         }
348
349         public Object JavaDoc next() {
350             Object JavaDoc result = entry;
351
352             if (result == null) {
353                 throw new NoSuchElementException JavaDoc();
354             } else {
355                 entry = entry.next;
356                 locateNext();
357                 return result;
358             }
359         }
360
361         public void remove() {
362             Entry remove = entry;
363             expunge();
364             entry = entry.next;
365             locateNext();
366
367             WeakIdentityHashMap.this.removeEntry(remove);
368         }
369     }
370
371     protected Iterator JavaDoc entryIterator() {
372         return new EntryIterator();
373     }
374
375     protected final int keyHash(Object JavaDoc key) {
376         return System.identityHashCode(key);
377     }
378
379     protected final boolean keyEquals(Object JavaDoc key1, Object JavaDoc key2) {
380         return key1 == key2;
381     }
382
383     public int size() {
384         expunge();
385         return super.size();
386     }
387     
388     public boolean isEmpty() {
389         return size() == 0;
390     }
391 }
392
Popular Tags