1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.castor.core.util;
17
18 import java.util.Collection;
19 import java.util.Map;
20 import java.util.Set;
21
22
23
24
25
26
27
28
29
30
31 public final class IdentityMap implements Map {
32
33
34
35 private static final int DEFAULT_CAPACITY = 17;
36
37
38 private static final float DEFAULT_LOAD = 0.75f;
39
40
41 private static final int DEFAULT_ENTRIES = (int) (DEFAULT_CAPACITY * DEFAULT_LOAD);
42
43
44 private static final int DEFAULT_INCREMENT = 2;
45
46
47 private static final int FIRST_PRIME_TO_CHECK = 3;
48
49
50
51
52 private int _capacity = DEFAULT_CAPACITY;
53
54
55 private int _maximum = DEFAULT_ENTRIES;
56
57
58 private Entry[] _buckets = new Entry[DEFAULT_CAPACITY];
59
60
61 private int _entries = 0;
62
63
64
65
66
67
68
69 public void clear() {
70 _capacity = DEFAULT_CAPACITY;
71 _maximum = DEFAULT_ENTRIES;
72 _buckets = new Entry[DEFAULT_CAPACITY];
73 _entries = 0;
74 }
75
76
77
78
79
80 public int size() { return _entries; }
81
82
83
84
85
86 public boolean isEmpty() { return (_entries == 0); }
87
88
89
90
91
92 public Object put(final Object key, final Object value) {
93 int hash = System.identityHashCode(key);
94 int index = hash % _capacity;
95 if (index < 0) { index = -index; }
96
97 Entry entry = _buckets[index];
98 Entry prev = null;
99 while (entry != null) {
100 if (entry.getKey() == key) {
101
102 Object ret = entry.getValue();
103 entry.setValue(value);
104 return ret;
105 }
106 prev = entry;
107 entry = entry.getNext();
108 }
109 if (prev == null) {
110
111 _buckets[index] = new Entry(key, hash, value);
112 } else {
113
114 prev.setNext(new Entry(key, hash, value));
115 }
116 _entries++;
117 if (_entries > _maximum) { rehash(); }
118 return null;
119 }
120
121
122
123
124 private void rehash() {
125 long nextCapacity = _capacity * DEFAULT_INCREMENT;
126 if (nextCapacity > Integer.MAX_VALUE) { return; }
127 nextCapacity = nextPrime(nextCapacity);
128 if (nextCapacity > Integer.MAX_VALUE) { return; }
129
130 int newCapacity = (int) nextCapacity;
131 Entry[] newBuckets = new Entry[newCapacity];
132
133 Entry entry = null;
134 Entry temp = null;
135 Entry next = null;
136 int newIndex = 0;
137
138 for (int index = 0; index < _capacity; index++) {
139 entry = _buckets[index];
140 while (entry != null) {
141 next = entry.getNext();
142
143 newIndex = entry.getHash() % newCapacity;
144 if (newIndex < 0) { newIndex = -newIndex; }
145
146 temp = newBuckets[newIndex];
147 if (temp == null) {
148
149 entry.setNext(null);
150 } else {
151
152 entry.setNext(temp);
153 }
154 newBuckets[newIndex] = entry;
155
156 entry = next;
157 }
158 }
159
160 _capacity = newCapacity;
161 _maximum = (int) (newCapacity * DEFAULT_LOAD);
162 _buckets = newBuckets;
163 }
164
165
166
167
168
169
170
171 private long nextPrime(final long minimum) {
172 long candidate = ((minimum + 1) / 2) * 2 + 1;
173 while (!isPrime(candidate)) { candidate += 2; }
174 return candidate;
175 }
176
177
178
179
180
181
182
183
184 private boolean isPrime(final long candidate) {
185 if ((candidate / 2) * 2 == candidate) { return false; }
186 long stop = candidate / 2;
187 for (long i = FIRST_PRIME_TO_CHECK; i < stop; i += 2) {
188 if ((candidate / i) * i == candidate) { return false; }
189 }
190 return true;
191 }
192
193
194
195
196
197 public boolean containsKey(final Object key) {
198 int hash = System.identityHashCode(key);
199 int index = hash % _capacity;
200 if (index < 0) { index = -index; }
201
202 Entry entry = _buckets[index];
203 while (entry != null) {
204 if (entry.getKey() == key) { return true; }
205 entry = entry.getNext();
206 }
207 return false;
208 }
209
210
211
212
213
214 public Object get(final Object key) {
215 int hash = System.identityHashCode(key);
216 int index = hash % _capacity;
217 if (index < 0) { index = -index; }
218
219 Entry entry = _buckets[index];
220 while (entry != null) {
221 if (entry.getKey() == key) { return entry.getValue(); }
222 entry = entry.getNext();
223 }
224 return null;
225 }
226
227
228
229
230
231 public Object remove(final Object key) {
232 int hash = System.identityHashCode(key);
233 int index = hash % _capacity;
234 if (index < 0) { index = -index; }
235
236 Entry entry = _buckets[index];
237 Entry prev = null;
238 while (entry != null) {
239 if (entry.getKey() == key) {
240
241 if (prev == null) {
242
243 _buckets[index] = entry.getNext();
244 } else {
245
246 prev.setNext(entry.getNext());
247 }
248 _entries--;
249 return entry.getValue();
250 }
251 prev = entry;
252 entry = entry.getNext();
253 }
254 return null;
255 }
256
257
258
259
260
261 public Set keySet() {
262 Set set = new IdentitySet(_capacity);
263
264 for (int i = 0; i < _capacity; i++) {
265 Entry entry = _buckets[i];
266 while (entry != null) {
267 set.add(entry.getKey());
268 entry = entry.getNext();
269 }
270 }
271
272 return set;
273 }
274
275
276
277
278
279
280
281
282 public Set entrySet() {
283 Set set = new IdentitySet(_capacity);
284
285 for (int i = 0; i < _capacity; i++) {
286 Entry entry = _buckets[i];
287 while (entry != null) {
288 set.add(entry);
289 entry = entry.getNext();
290 }
291 }
292
293 return set;
294 }
295
296
297
298
299
300
301
302
303 public Collection values() {
304 throw new UnsupportedOperationException();
305 }
306
307
308
309
310
311
312
313
314 public boolean containsValue(final Object value) {
315 throw new UnsupportedOperationException();
316 }
317
318
319
320
321
322
323
324
325
326 public void putAll(final Map map) {
327 throw new UnsupportedOperationException();
328 }
329
330
331
332
333
334
335 public final class Entry implements Map.Entry {
336
337 private Object _key;
338
339
340 private int _hash;
341
342
343 private Object _value;
344
345
346 private Entry _next = null;
347
348
349
350
351
352
353
354
355 public Entry(final Object key, final int hash, final Object value) {
356 _key = key;
357 _hash = hash;
358 _value = value;
359 }
360
361
362
363
364
365
366 public Object getKey() { return _key; }
367
368
369
370
371
372
373 public int getHash() { return _hash; }
374
375
376
377
378
379
380
381 public Object setValue(final Object value) {
382 Object temp = _value;
383 _value = value;
384 return temp;
385 }
386
387
388
389
390
391
392 public Object getValue() { return _value; }
393
394
395
396
397
398
399 public void setNext(final Entry next) { _next = next; }
400
401
402
403
404
405
406 public Entry getNext() { return _next; }
407 }
408
409
410 }