1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.castor.core.util;
15
16 import java.util.Collection;
17 import java.util.Iterator;
18 import java.util.NoSuchElementException;
19 import java.util.Set;
20
21
22
23
24
25
26
27
28
29 public final class IdentitySet implements Set {
30
31
32
33 private static final int DEFAULT_CAPACITY = 17;
34
35
36 private static final float DEFAULT_LOAD = 0.75f;
37
38
39 private static final int DEFAULT_ENTRIES = (int) (DEFAULT_CAPACITY * DEFAULT_LOAD);
40
41
42 private static final int DEFAULT_INCREMENT = 2;
43
44
45 private static final int FIRST_PRIME_TO_CHECK = 3;
46
47
48
49
50 private int _capacity;
51
52
53 private int _maximum;
54
55
56 private Entry[] _buckets;
57
58
59 private int _entries = 0;
60
61
62
63
64
65
66 public IdentitySet() {
67 _capacity = DEFAULT_CAPACITY;
68 _maximum = DEFAULT_ENTRIES;
69 _buckets = new Entry[DEFAULT_CAPACITY];
70 }
71
72
73
74
75
76
77 public IdentitySet(final int capacity) {
78 _capacity = capacity;
79 _maximum = (int) (capacity * DEFAULT_LOAD);
80 _buckets = new Entry[capacity];
81 }
82
83
84
85
86
87
88 public void clear() {
89 _capacity = DEFAULT_CAPACITY;
90 _maximum = DEFAULT_ENTRIES;
91 _buckets = new Entry[DEFAULT_CAPACITY];
92 _entries = 0;
93 }
94
95
96
97
98
99
100 public int size() {
101 return _entries;
102 }
103
104
105
106
107
108
109 public boolean isEmpty() {
110 return (_entries == 0);
111 }
112
113
114
115
116
117
118 public boolean add(final Object key) {
119 int hash = System.identityHashCode(key);
120 int index = hash % _capacity;
121 if (index < 0) {
122 index = -index;
123 }
124
125 Entry entry = _buckets[index];
126 Entry prev = null;
127 while (entry != null) {
128 if (entry.getKey() == key) {
129
130 return false;
131 }
132 prev = entry;
133 entry = entry.getNext();
134 }
135 if (prev == null) {
136
137 _buckets[index] = new Entry(key, hash);
138 } else {
139
140 prev.setNext(new Entry(key, hash));
141 }
142 _entries++;
143 if (_entries > _maximum) {
144 rehash();
145 }
146 return true;
147 }
148
149
150
151
152 private void rehash() {
153 long nextCapacity = _capacity * DEFAULT_INCREMENT;
154 if (nextCapacity > Integer.MAX_VALUE) {
155 return;
156 }
157 nextCapacity = nextPrime(nextCapacity);
158 if (nextCapacity > Integer.MAX_VALUE) {
159 return;
160 }
161
162 int newCapacity = (int) nextCapacity;
163 Entry[] newBuckets = new Entry[newCapacity];
164
165 Entry entry = null;
166 Entry temp = null;
167 Entry next = null;
168 int newIndex = 0;
169
170 for (int index = 0; index < _capacity; index++) {
171 entry = _buckets[index];
172 while (entry != null) {
173 next = entry.getNext();
174
175 newIndex = entry.getHash() % newCapacity;
176 if (newIndex < 0) {
177 newIndex = -newIndex;
178 }
179
180 temp = newBuckets[newIndex];
181 if (temp == null) {
182
183 entry.setNext(null);
184 } else {
185
186 entry.setNext(temp);
187 }
188 newBuckets[newIndex] = entry;
189
190 entry = next;
191 }
192 }
193
194 _capacity = newCapacity;
195 _maximum = (int) (newCapacity * DEFAULT_LOAD);
196 _buckets = newBuckets;
197 }
198
199
200
201
202
203
204
205 private long nextPrime(final long minimum) {
206 long candidate = ((minimum + 1) / 2) * 2 + 1;
207 while (!isPrime(candidate)) {
208 candidate += 2;
209 }
210 return candidate;
211 }
212
213
214
215
216
217
218
219 private boolean isPrime(final long candidate) {
220 if ((candidate / 2) * 2 == candidate) {
221 return false;
222 }
223 long stop = candidate / 2;
224 for (long i = FIRST_PRIME_TO_CHECK; i < stop; i += 2) {
225 if ((candidate / i) * i == candidate) {
226 return false;
227 }
228 }
229 return true;
230 }
231
232
233
234
235
236
237 public boolean contains(final Object key) {
238 int hash = System.identityHashCode(key);
239 int index = hash % _capacity;
240 if (index < 0) {
241 index = -index;
242 }
243
244 Entry entry = _buckets[index];
245 while (entry != null) {
246 if (entry.getKey() == key) {
247 return true;
248 }
249 entry = entry.getNext();
250 }
251 return false;
252 }
253
254
255
256
257
258
259 public boolean remove(final Object key) {
260 int hash = System.identityHashCode(key);
261 int index = hash % _capacity;
262 if (index < 0) {
263 index = -index;
264 }
265
266 Entry entry = _buckets[index];
267 Entry prev = null;
268 while (entry != null) {
269 if (entry.getKey() == key) {
270
271 if (prev == null) {
272
273 _buckets[index] = entry.getNext();
274 } else {
275
276 prev.setNext(entry.getNext());
277 }
278 _entries--;
279 return true;
280 }
281 prev = entry;
282 entry = entry.getNext();
283 }
284 return false;
285 }
286
287
288
289
290
291
292 public Iterator iterator() {
293 return new IdentityIterator();
294 }
295
296
297
298
299
300
301 public Object[] toArray() {
302 Object[] result = new Object[_entries];
303
304 int j = 0;
305 for (int i = 0; i < _capacity; i++) {
306 Entry entry = _buckets[i];
307 while (entry != null) {
308 result[j++] = entry.getKey();
309 entry = entry.getNext();
310 }
311 }
312
313 return result;
314 }
315
316
317
318
319
320
321 public Object[] toArray(final Object[] a) {
322 Object[] result = a;
323 if (result.length < _entries) {
324 result = (Object[]) java.lang.reflect.Array.newInstance(result.getClass().getComponentType(),
325 _entries);
326 }
327
328 int j = 0;
329 for (int i = 0; i < _capacity; i++) {
330 Entry entry = _buckets[i];
331 while (entry != null) {
332 result[j++] = entry.getKey();
333 entry = entry.getNext();
334 }
335 }
336
337 while (j < result.length) {
338 result[j++] = null;
339 }
340
341 return result;
342 }
343
344
345
346
347
348
349
350
351
352 public boolean containsAll(final Collection c) {
353 throw new UnsupportedOperationException();
354 }
355
356
357
358
359
360
361
362
363
364 public boolean addAll(final Collection c) {
365 throw new UnsupportedOperationException();
366 }
367
368
369
370
371
372
373
374
375
376 public boolean removeAll(final Collection c) {
377 throw new UnsupportedOperationException();
378 }
379
380
381
382
383
384
385
386
387
388 public boolean retainAll(final Collection c) {
389 throw new UnsupportedOperationException();
390 }
391
392
393
394
395
396
397 public final class Entry {
398
399 private Object _key;
400
401
402 private int _hash;
403
404
405 private Entry _next = null;
406
407
408
409
410
411
412
413 public Entry(final Object key, final int hash) {
414 _key = key;
415 _hash = hash;
416 }
417
418
419
420
421
422
423 public Object getKey() {
424 return _key;
425 }
426
427
428
429
430
431
432 public int getHash() {
433 return _hash;
434 }
435
436
437
438
439
440
441 public void setNext(final Entry next) {
442 _next = next;
443 }
444
445
446
447
448
449
450 public Entry getNext() {
451 return _next;
452 }
453 }
454
455
456
457
458
459
460 private class IdentityIterator implements Iterator {
461
462 private int _index = 0;
463
464
465 private Entry _next = _buckets[0];
466
467
468
469
470 public IdentityIterator() {
471 if (_entries > 0) {
472 while ((_next == null) && (++_index < _capacity)) {
473 _next = _buckets[_index];
474 }
475 }
476 }
477
478
479
480
481
482
483 public boolean hasNext() {
484 return (_next != null);
485 }
486
487
488
489
490
491
492 public Object next() {
493 Entry entry = _next;
494 if (entry == null) {
495 throw new NoSuchElementException();
496 }
497
498 _next = entry.getNext();
499 while ((_next == null) && (++_index < _capacity)) {
500 _next = _buckets[_index];
501 }
502
503 return entry.getKey();
504 }
505
506
507
508
509
510
511
512 public void remove() {
513 throw new UnsupportedOperationException();
514 }
515 }
516
517
518 }