comparison tango/tango/util/collection/HashSet.d @ 132:1700239cab2e trunk

[svn r136] MAJOR UNSTABLE UPDATE!!! Initial commit after moving to Tango instead of Phobos. Lots of bugfixes... This build is not suitable for most things.
author lindquist
date Fri, 11 Jan 2008 17:57:40 +0100
parents
children
comparison
equal deleted inserted replaced
131:5825d48b27d1 132:1700239cab2e
1 /*
2 File: HashSet.d
3
4 Originally written by Doug Lea and released into the public domain.
5 Thanks for the assistance and support of Sun Microsystems Labs, Agorics
6 Inc, Loral, and everyone contributing, testing, and using this code.
7
8 History:
9 Date Who What
10 24Sep95 dl@cs.oswego.edu Create from tango.util.collection.d working file
11 13Oct95 dl Changed protection statuses
12
13 */
14
15
16 module tango.util.collection.HashSet;
17
18 private import tango.core.Exception;
19
20 private import tango.util.collection.model.Iterator,
21 tango.util.collection.model.HashParams,
22 tango.util.collection.model.GuardIterator;
23
24 private import tango.util.collection.impl.LLCell,
25 tango.util.collection.impl.SetCollection,
26 tango.util.collection.impl.AbstractIterator;
27
28
29 /**
30 *
31 * Hash table implementation of set
32 *
33 author: Doug Lea
34 * @version 0.93
35 *
36 * <P> For an introduction to this package see <A HREF="index.html"> Overview </A>.
37 **/
38
39 public class HashSet(T) : SetCollection!(T), HashParams
40 {
41 private alias LLCell!(T) LLCellT;
42
43 alias SetCollection!(T).remove remove;
44 alias SetCollection!(T).removeAll removeAll;
45
46
47 // instance variables
48
49 /**
50 * The table. Each entry is a list. Null if no table allocated
51 **/
52 private LLCellT table[];
53 /**
54 * The threshold load factor
55 **/
56 private float loadFactor;
57
58
59 // constructors
60
61 /**
62 * Make an empty HashedSet.
63 **/
64
65 public this ()
66 {
67 this(null, defaultLoadFactor);
68 }
69
70 /**
71 * Make an empty HashedSet using given element screener
72 **/
73
74 public this (Predicate screener)
75 {
76 this(screener, defaultLoadFactor);
77 }
78
79 /**
80 * Special version of constructor needed by clone()
81 **/
82
83 protected this (Predicate s, float f)
84 {
85 super(s);
86 table = null;
87 loadFactor = f;
88 }
89
90 /**
91 * Make an independent copy of the table. Does not clone elements.
92 **/
93
94 public final HashSet duplicate()
95 {
96 auto c = new HashSet (screener, loadFactor);
97
98 if (count !is 0)
99 {
100 int cap = 2 * cast(int)(count / loadFactor) + 1;
101 if (cap < defaultInitialBuckets)
102 cap = defaultInitialBuckets;
103
104 c.buckets(cap);
105 for (int i = 0; i < table.length; ++i)
106 for (LLCellT p = table[i]; p !is null; p = p.next())
107 c.add(p.element());
108 }
109 return c;
110 }
111
112
113 // HashTableParams methods
114
115 /**
116 * Implements tango.util.collection.HashTableParams.buckets.
117 * Time complexity: O(1).
118 * See_Also: tango.util.collection.HashTableParams.buckets.
119 **/
120
121 public final int buckets()
122 {
123 return (table is null) ? 0 : table.length;
124 }
125
126 /**
127 * Implements tango.util.collection.HashTableParams.buckets.
128 * Time complexity: O(n).
129 * See_Also: tango.util.collection.HashTableParams.buckets.
130 **/
131
132 public final void buckets(int newCap)
133 {
134 if (newCap is buckets())
135 return ;
136 else
137 if (newCap >= 1)
138 resize(newCap);
139 else
140 {
141 char[16] tmp;
142 throw new IllegalArgumentException("Impossible Hash table size:" ~ itoa(tmp, newCap));
143 }
144 }
145
146 /**
147 * Implements tango.util.collection.HashTableParams.thresholdLoadfactor
148 * Time complexity: O(1).
149 * See_Also: tango.util.collection.HashTableParams.thresholdLoadfactor
150 **/
151
152 public final float thresholdLoadFactor()
153 {
154 return loadFactor;
155 }
156
157 /**
158 * Implements tango.util.collection.HashTableParams.thresholdLoadfactor
159 * Time complexity: O(n).
160 * See_Also: tango.util.collection.HashTableParams.thresholdLoadfactor
161 **/
162
163 public final void thresholdLoadFactor(float desired)
164 {
165 if (desired > 0.0)
166 {
167 loadFactor = desired;
168 checkLoadFactor();
169 }
170 else
171 throw new IllegalArgumentException("Invalid Hash table load factor");
172 }
173
174
175
176
177
178 // Collection methods
179
180 /**
181 * Implements tango.util.collection.impl.Collection.Collection.contains
182 * Time complexity: O(1) average; O(n) worst.
183 * See_Also: tango.util.collection.impl.Collection.Collection.contains
184 **/
185 public final bool contains(T element)
186 {
187 if (!isValidArg(element) || count is 0)
188 return false;
189
190 LLCellT p = table[hashOf(element)];
191 if (p !is null)
192 return p.find(element) !is null;
193 else
194 return false;
195 }
196
197 /**
198 * Implements tango.util.collection.impl.Collection.Collection.instances
199 * Time complexity: O(n).
200 * See_Also: tango.util.collection.impl.Collection.Collection.instances
201 **/
202 public final uint instances(T element)
203 {
204 if (contains(element))
205 return 1;
206 else
207 return 0;
208 }
209
210 /**
211 * Implements tango.util.collection.impl.Collection.Collection.elements
212 * Time complexity: O(1).
213 * See_Also: tango.util.collection.impl.Collection.Collection.elements
214 **/
215 public final GuardIterator!(T) elements()
216 {
217 return new CellIterator!(T)(this);
218 }
219
220 /**
221 * Implements tango.util.collection.model.View.View.opApply
222 * Time complexity: O(n).
223 * See_Also: tango.util.collection.model.View.View.opApply
224 **/
225 int opApply (int delegate (inout T value) dg)
226 {
227 auto scope iterator = new CellIterator!(T)(this);
228 return iterator.opApply (dg);
229 }
230
231 // MutableCollection methods
232
233 /**
234 * Implements tango.util.collection.impl.Collection.Collection.clear.
235 * Time complexity: O(1).
236 * See_Also: tango.util.collection.impl.Collection.Collection.clear
237 **/
238 public final void clear()
239 {
240 setCount(0);
241 table = null;
242 }
243
244 /**
245 * Implements tango.util.collection.impl.Collection.Collection.exclude.
246 * Time complexity: O(1) average; O(n) worst.
247 * See_Also: tango.util.collection.impl.Collection.Collection.exclude
248 **/
249 public final void removeAll(T element)
250 {
251 remove(element);
252 }
253
254 public final void remove(T element)
255 {
256 if (!isValidArg(element) || count is 0)
257 return ;
258
259 int h = hashOf(element);
260 LLCellT hd = table[h];
261 LLCellT p = hd;
262 LLCellT trail = p;
263
264 while (p !is null)
265 {
266 LLCellT n = p.next();
267 if (p.element() == (element))
268 {
269 decCount();
270 if (p is table[h])
271 {
272 table[h] = n;
273 trail = n;
274 }
275 else
276 trail.next(n);
277 return ;
278 }
279 else
280 {
281 trail = p;
282 p = n;
283 }
284 }
285 }
286
287 public final void replace(T oldElement, T newElement)
288 {
289
290 if (count is 0 || !isValidArg(oldElement) || oldElement == (newElement))
291 return ;
292
293 if (contains(oldElement))
294 {
295 checkElement(newElement);
296 remove(oldElement);
297 add(newElement);
298 }
299 }
300
301 public final void replaceAll(T oldElement, T newElement)
302 {
303 replace(oldElement, newElement);
304 }
305
306 /**
307 * Implements tango.util.collection.impl.Collection.Collection.take.
308 * Time complexity: O(number of buckets).
309 * See_Also: tango.util.collection.impl.Collection.Collection.take
310 **/
311 public final T take()
312 {
313 if (count !is 0)
314 {
315 for (int i = 0; i < table.length; ++i)
316 {
317 if (table[i] !is null)
318 {
319 decCount();
320 auto v = table[i].element();
321 table[i] = table[i].next();
322 return v;
323 }
324 }
325 }
326
327 checkIndex(0);
328 return T.init; // not reached
329 }
330
331
332 // MutableSet methods
333
334 /**
335 * Implements tango.util.collection.impl.SetCollection.SetCollection.add.
336 * Time complexity: O(1) average; O(n) worst.
337 * See_Also: tango.util.collection.impl.SetCollection.SetCollection.add
338 **/
339 public final void add(T element)
340 {
341 checkElement(element);
342
343 if (table is null)
344 resize(defaultInitialBuckets);
345
346 int h = hashOf(element);
347 LLCellT hd = table[h];
348 if (hd !is null && hd.find(element) !is null)
349 return ;
350
351 LLCellT n = new LLCellT(element, hd);
352 table[h] = n;
353 incCount();
354
355 if (hd !is null)
356 checkLoadFactor(); // only check if bin was nonempty
357 }
358
359
360
361 // Helper methods
362
363 /**
364 * Check to see if we are past load factor threshold. If so, resize
365 * so that we are at half of the desired threshold.
366 * Also while at it, check to see if we are empty so can just
367 * unlink table.
368 **/
369 protected final void checkLoadFactor()
370 {
371 if (table is null)
372 {
373 if (count !is 0)
374 resize(defaultInitialBuckets);
375 }
376 else
377 {
378 float fc = cast(float) (count);
379 float ft = table.length;
380 if (fc / ft > loadFactor)
381 {
382 int newCap = 2 * cast(int)(fc / loadFactor) + 1;
383 resize(newCap);
384 }
385 }
386 }
387
388 /**
389 * Mask off and remainder the hashCode for element
390 * so it can be used as table index
391 **/
392
393 protected final int hashOf(T element)
394 {
395 return (typeid(T).getHash(&element) & 0x7FFFFFFF) % table.length;
396 }
397
398
399 /**
400 * resize table to new capacity, rehashing all elements
401 **/
402 protected final void resize(int newCap)
403 {
404 LLCellT newtab[] = new LLCellT[newCap];
405
406 if (table !is null)
407 {
408 for (int i = 0; i < table.length; ++i)
409 {
410 LLCellT p = table[i];
411 while (p !is null)
412 {
413 LLCellT n = p.next();
414 int h = (p.elementHash() & 0x7FFFFFFF) % newCap;
415 p.next(newtab[h]);
416 newtab[h] = p;
417 p = n;
418 }
419 }
420 }
421
422 table = newtab;
423 incVersion();
424 }
425
426 /+
427 private final void readObject(java.io.ObjectInputStream stream)
428
429 {
430 int len = stream.readInt();
431
432 if (len > 0)
433 table = new LLCellT[len];
434 else
435 table = null;
436
437 loadFactor = stream.readFloat();
438 int count = stream.readInt();
439
440 while (count-- > 0)
441 {
442 T element = stream.readObject();
443 int h = hashOf(element);
444 LLCellT hd = table[h];
445 LLCellT n = new LLCellT(element, hd);
446 table[h] = n;
447 }
448 }
449
450 private final void writeObject(java.io.ObjectOutputStream stream)
451 {
452 int len;
453
454 if (table !is null)
455 len = table.length;
456 else
457 len = 0;
458
459 stream.writeInt(len);
460 stream.writeFloat(loadFactor);
461 stream.writeInt(count);
462
463 if (len > 0)
464 {
465 Iterator e = elements();
466 while (e.more())
467 stream.writeObject(e.value());
468 }
469 }
470
471 +/
472
473 // ImplementationCheckable methods
474
475 /**
476 * Implements tango.util.collection.model.View.View.checkImplementation.
477 * See_Also: tango.util.collection.model.View.View.checkImplementation
478 **/
479 public override void checkImplementation()
480 {
481 super.checkImplementation();
482
483 assert(!(table is null && count !is 0));
484 assert((table is null || table.length > 0));
485 assert(loadFactor > 0.0f);
486
487 if (table !is null)
488 {
489 int c = 0;
490 for (int i = 0; i < table.length; ++i)
491 {
492 for (LLCellT p = table[i]; p !is null; p = p.next())
493 {
494 ++c;
495 assert(allows(p.element()));
496 assert(contains(p.element()));
497 assert(instances(p.element()) is 1);
498 assert(hashOf(p.element()) is i);
499 }
500 }
501 assert(c is count);
502 }
503 }
504
505
506
507 /***********************************************************************
508
509 opApply() has migrated here to mitigate the virtual call
510 on method get()
511
512 ************************************************************************/
513
514 private static class CellIterator(T) : AbstractIterator!(T)
515 {
516 private int row;
517 private LLCellT cell;
518 private LLCellT[] table;
519
520 public this (HashSet set)
521 {
522 super (set);
523 table = set.table;
524 }
525
526 public final T get()
527 {
528 decRemaining();
529
530 while (cell is null)
531 cell = table [row++];
532
533 auto v = cell.element();
534 cell = cell.next();
535 return v;
536 }
537
538 int opApply (int delegate (inout T value) dg)
539 {
540 int result;
541
542 for (auto i=remaining(); i--;)
543 {
544 auto value = get();
545 if ((result = dg(value)) != 0)
546 break;
547 }
548 return result;
549 }
550 }
551 }
552
553
554
555 debug (Test)
556 {
557 import tango.io.Console;
558
559 void main()
560 {
561 auto set = new HashSet!(char[]);
562 set.add ("foo");
563 set.add ("bar");
564 set.add ("wumpus");
565
566 foreach (value; set.elements) {}
567
568 auto elements = set.elements();
569 while (elements.more)
570 auto v = elements.get();
571
572 set.checkImplementation();
573
574 foreach (value; set)
575 Cout (value).newline;
576 }
577 }