Download Reference Manual
The Developer's Library for D
About Wiki Forums Source Search Contact

root/trunk/tango/util/container/HashSet.d

Revision 4008, 27.8 kB (checked in by larsivi, 1 month ago)

Fixed eol style for container package, refs #1303

  • Property svn:eol-style set to native
Line 
1 /*******************************************************************************
2
3         copyright:      Copyright (c) 2008 Kris Bell. All rights reserved
4
5         license:        BSD style: $(LICENSE)
6
7         version:        Apr 2008: Initial release
8
9         authors:        Kris
10
11         Since:          0.99.7
12
13         Based upon Doug Lea's Java collection package
14
15 *******************************************************************************/
16
17 module tango.util.container.HashSet;
18
19 private import  tango.util.container.Slink;
20
21 public  import  tango.util.container.Container;
22
23 private import tango.util.container.model.IContainer;
24
25 /*******************************************************************************
26
27         Hash table implementation of a Set
28
29         ---
30         Iterator iterator ()
31         int opApply (int delegate(ref V value) dg)
32
33         bool add (V element)
34         bool contains (V element)
35         bool take (ref V element)
36         bool remove (V element)
37         uint remove (IContainer!(V) e)
38         bool replace (V oldElement, V newElement)
39
40         uint size ()
41         bool isEmpty ()
42         V[] toArray (V[] dst)
43         HashSet dup ()
44         HashSet clear ()
45         HashSet reset ()
46
47         uint buckets ()
48         void buckets (uint cap)
49         float threshold ()
50         void threshold (float desired)
51         ---
52
53 *******************************************************************************/
54
55 class HashSet (V, alias Hash = Container.hash,
56                   alias Reap = Container.reap,
57                   alias Heap = Container.Collect)
58                   : IContainer!(V)
59 {
60         // use this type for Allocator configuration
61         public alias Slink!(V)  Type;
62        
63         private alias Type      *Ref;
64
65         private alias Heap!(Type) Alloc;
66
67         // Each table entry is a list - null if no table allocated
68         private Ref             table[];
69        
70         // number of elements contained
71         private uint            count;
72
73         // the threshold load factor
74         private float           loadFactor;
75
76         // configured heap manager
77         private Alloc           heap;
78        
79         // mutation tag updates on each change
80         private uint            mutation;
81
82         /***********************************************************************
83
84                 Construct a HashSet instance
85
86         ***********************************************************************/
87
88         this (float f = Container.defaultLoadFactor)
89         {
90                 loadFactor = f;
91         }
92
93         /***********************************************************************
94
95                 Clean up when deleted
96
97         ***********************************************************************/
98
99         ~this ()
100         {
101                 reset;
102         }
103
104         /***********************************************************************
105
106                 Return the configured allocator
107                 
108         ***********************************************************************/
109
110         final Alloc allocator ()
111         {
112                 return heap;
113         }
114
115         /***********************************************************************
116
117                 Return a generic iterator for contained elements
118                 
119         ***********************************************************************/
120
121         final Iterator iterator ()
122         {
123                 Iterator i = void;
124                 i.mutation = mutation;
125                 i.table = table;
126                 i.owner = this;
127                 i.cell = null;
128                 i.row = 0;
129                 return i;
130         }
131
132         /***********************************************************************
133
134
135         ***********************************************************************/
136
137         final int opApply (int delegate(ref V value) dg)
138         {
139                 return iterator.opApply (dg);
140         }
141
142         /***********************************************************************
143
144                 Return the number of elements contained
145                 
146         ***********************************************************************/
147
148         final uint size ()
149         {
150                 return count;
151         }
152        
153         /***********************************************************************
154
155                 Add a new element to the set. Does not add if there is an
156                 equivalent already present. Returns true where an element
157                 is added, false where it already exists
158                 
159                 Time complexity: O(1) average; O(n) worst.
160                 
161         ***********************************************************************/
162
163         final bool add (V element)
164         {
165                 if (table is null)
166                     resize (Container.defaultInitialBuckets);
167
168                 auto h = Hash  (element, table.length);
169                 auto hd = table[h];
170
171                 if (hd && hd.find (element))
172                     return false;
173
174                 table[h] = allocate.set (element, hd);
175                 increment;
176
177                 // only check if bin was nonempty                   
178                 if (hd)
179                     checkLoad;
180                 return true;
181         }
182
183         /***********************************************************************
184
185                 Does this set contain the given element?
186         
187                 Time complexity: O(1) average; O(n) worst
188                 
189         ***********************************************************************/
190
191         final bool contains (V element)
192         {
193                 if (count)
194                    {
195                    auto p = table[Hash (element, table.length)];
196                    if (p && p.find (element))
197                        return true;
198                    }
199                 return false;
200         }
201
202         /***********************************************************************
203
204                 Make an independent copy of the container. Does not clone
205                 elements
206                 
207                 Time complexity: O(n)
208                 
209         ***********************************************************************/
210
211         final HashSet dup ()
212         {
213                 auto clone = new HashSet!(V, Hash, Reap, Heap) (loadFactor);
214                
215                 if (count)
216                    {
217                    clone.buckets (buckets);
218
219                    foreach (value; iterator)
220                             clone.add (value);
221                    }
222                 return clone;
223         }
224
225         /***********************************************************************
226
227                 Remove the provided element. Returns true if found, false
228                 otherwise
229                 
230                 Time complexity: O(1) average; O(n) worst
231
232         ***********************************************************************/
233
234         final uint remove (V element, bool all)
235         {
236                 return remove(element) ? 1 : 0;
237         }
238
239         /***********************************************************************
240
241                 Remove the provided element. Returns true if found, false
242                 otherwise
243                 
244                 Time complexity: O(1) average; O(n) worst
245
246         ***********************************************************************/
247
248         final bool remove (V element)
249         {
250                 if (count)
251                    {
252                    auto h = Hash (element, table.length);
253                    auto hd = table[h];
254                    auto trail = hd;
255                    auto p = hd;
256
257                    while (p)
258                          {
259                          auto n = p.next;
260                          if (element == p.value)
261                             {
262                             decrement (p);
263                             if (p is table[h])
264                                {
265                                table[h] = n;
266                                trail = n;
267                                }
268                             else
269                                trail.next = n;
270                             return true;
271                             }
272                          else
273                             {
274                             trail = p;
275                             p = n;
276                             }
277                          }
278                    }
279                 return false;
280         }
281
282         /***********************************************************************
283
284                 Replace the first instance of oldElement with newElement.
285                 Returns true if oldElement was found and replaced, false
286                 otherwise.
287                 
288         ***********************************************************************/
289
290         final uint replace (V oldElement, V newElement, bool all)
291         {
292                 return replace (oldElement, newElement) ? 1 : 0;
293         }
294
295         /***********************************************************************
296
297                 Replace the first instance of oldElement with newElement.
298                 Returns true if oldElement was found and replaced, false
299                 otherwise.
300                 
301         ***********************************************************************/
302
303         final bool replace (V oldElement, V newElement)
304         {
305
306                 if (count && oldElement != newElement)
307                    if (contains (oldElement))
308                       {
309                       remove (oldElement);
310                       add (newElement);
311                       return true;
312                       }
313                 return false;
314         }
315
316         /***********************************************************************
317
318                 Remove and expose the first element. Returns false when no
319                 more elements are contained
320         
321                 Time complexity: O(n)
322
323         ***********************************************************************/
324
325         final bool take (ref V element)
326         {
327                 if (count)
328                     foreach (ref list; table)
329                              if (list)
330                                 {
331                                 auto p = list;
332                                 element = p.value;
333                                 list = p.next;
334                                 decrement (p);
335                                 return true;
336                                 }
337                 return false;
338         }
339
340         /***********************************************************************
341
342         ************************************************************************/
343
344         public void add (IContainer!(V) e)
345         {
346                 foreach (value; e)
347                          add (value);
348         }
349
350         /***********************************************************************
351
352         ************************************************************************/
353
354         public uint remove (IContainer!(V) e)
355         {
356                 uint c;
357                 foreach (value; e)
358                          if (remove (value))
359                              ++c;
360                 return c;
361         }
362
363         /***********************************************************************
364
365                 Clears the HashMap contents. Various attributes are
366                 retained, such as the internal table itself. Invoke
367                 reset() to drop everything.
368
369                 Time complexity: O(n)
370                 
371         ***********************************************************************/
372
373         final HashSet clear ()
374         {
375                 return clear (false);
376         }
377
378         /***********************************************************************
379
380                 Reset the HashSet contents and optionally configure a new
381                 heap manager. This releases more memory than clear() does
382
383                 Time complexity: O(1)
384                 
385         ***********************************************************************/
386
387         final HashSet reset ()
388         {
389                 clear (true);
390                 heap.collect (table);
391                 table = null;
392                 return this;
393         }
394
395         /***********************************************************************
396
397                 Return the number of buckets
398
399                 Time complexity: O(1)
400
401         ***********************************************************************/
402
403         final uint buckets ()
404         {
405                 return table ? table.length : 0;
406         }
407
408         /***********************************************************************
409
410                 Set the number of buckets and resize as required
411                 
412                 Time complexity: O(n)
413
414         ***********************************************************************/
415
416         final void buckets (uint cap)
417         {
418                 if (cap < Container.defaultInitialBuckets)
419                     cap = Container.defaultInitialBuckets;
420
421                 if (cap !is buckets)
422                     resize (cap);
423         }
424
425         /***********************************************************************
426
427                 Return the resize threshold
428                 
429                 Time complexity: O(1)
430
431         ***********************************************************************/
432
433         final float threshold ()
434         {
435                 return loadFactor;
436         }
437
438         /***********************************************************************
439
440                 Set the resize threshold, and resize as required
441                 
442                 Time complexity: O(n)
443                 
444         ***********************************************************************/
445
446         final void threshold (float desired)
447         {
448                 assert (desired > 0.0);
449                 loadFactor = desired;
450                 if (table)
451                     checkLoad;
452         }
453
454         /***********************************************************************
455
456                 Copy and return the contained set of values in an array,
457                 using the optional dst as a recipient (which is resized
458                 as necessary).
459
460                 Returns a slice of dst representing the container values.
461                 
462                 Time complexity: O(n)
463                 
464         ***********************************************************************/
465
466         final V[] toArray (V[] dst = null)
467         {
468                 if (dst.length < count)
469                     dst.length = count;
470
471                 int i = 0;
472                 foreach (v; this)
473                          dst[i++] = v;
474                 return dst [0 .. count];                       
475         }
476
477         /***********************************************************************
478
479                 Is this container empty?
480                 
481                 Time complexity: O(1)
482                 
483         ***********************************************************************/
484
485         final bool isEmpty ()
486         {
487                 return count is 0;
488         }
489
490         /***********************************************************************
491
492                 Sanity check
493                 
494         ***********************************************************************/
495
496         final HashSet check()
497         {
498                 assert(!(table is null && count !is 0));
499                 assert((table is null || table.length > 0));
500                 assert(loadFactor > 0.0f);
501
502                 if (table)
503                    {
504                    int c = 0;
505                    for (int i = 0; i < table.length; ++i)
506                        {
507                        for (auto p = table[i]; p; p = p.next)
508                            {
509                            ++c;
510                            assert(contains(p.value));
511                            assert(Hash (p.value, table.length) is i);
512                            }
513                        }
514                    assert(c is count);
515                    }
516                 return this;
517         }
518
519         /***********************************************************************
520
521                 Allocate a node instance. This is used as the default allocator
522                 
523         ***********************************************************************/
524
525         private Ref allocate ()
526         {
527                 return heap.allocate;
528         }
529        
530         /***********************************************************************
531
532                  Check to see if we are past load factor threshold. If so,
533                  resize so that we are at half of the desired threshold.
534                 
535         ***********************************************************************/
536
537         private void checkLoad ()
538         {
539                 float fc = count;
540                 float ft = table.length;
541                 if (fc / ft > loadFactor)
542                     resize (2 * cast(int)(fc / loadFactor) + 1);
543         }
544
545         /***********************************************************************
546
547                 resize table to new capacity, rehashing all elements
548                 
549         ***********************************************************************/
550
551         private void resize (uint newCap)
552         {
553                 //Stdout.formatln ("resize {}", newCap);
554                 auto newtab = heap.allocate (newCap);
555                 mutate;
556
557                 foreach (bucket; table)
558                          while (bucket)
559                                {
560                                auto n = bucket.next;
561                                auto h = Hash (bucket.value, newCap);
562                                bucket.next = newtab[h];
563                                newtab[h] = bucket;
564                                bucket = n;
565                                }
566
567                 // release the prior table and assign new one
568                 heap.collect (table);
569                 table = newtab;
570         }
571
572         /***********************************************************************
573
574                 Remove the indicated node. We need to traverse buckets
575                 for this, since we're singly-linked only. Better to save
576                 the per-node memory than to gain a little on each remove
577
578                 Used by iterators only
579                 
580         ***********************************************************************/
581
582         private bool remove (Ref node, uint row)
583         {
584                 auto hd = table[row];
585                 auto trail = hd;
586                 auto p = hd;
587
588                 while (p)
589                       {
590                       auto n = p.next;
591                       if (p is node)
592                          {
593                          decrement (p);
594                          if (p is hd)
595                              table[row] = n;
596                          else
597                             trail.next = n;
598                          return true;
599                          }
600                       else
601                          {
602                          trail = p;
603                          p = n;
604                          }
605                       }
606                 return false;
607         }
608
609         /***********************************************************************
610
611                 Clears the HashSet contents. Various attributes are
612                 retained, such as the internal table itself. Invoke
613                 reset() to drop everything.
614
615                 Time complexity: O(n)
616                 
617         ***********************************************************************/
618
619         private HashSet clear (bool all)
620         {
621                 mutate;
622
623                 // collect each node if we can't collect all at once
624                 if (heap.collect(all) is false)
625                     foreach (ref v; table)
626                              while (v)
627                                    {
628                                    auto n = v.next;
629                                    decrement (v);
630                                    v = n;
631                                    }
632
633                 // retain table, but remove bucket chains
634                 foreach (ref v; table)
635                          v = null;
636
637                 count = 0;
638                 return this;
639         }
640
641         /***********************************************************************
642
643                 new element was added
644                 
645         ***********************************************************************/
646
647         private void increment()
648         {
649                 ++mutation;
650                 ++count;
651         }
652        
653         /***********************************************************************
654
655                 element was removed
656                 
657         ***********************************************************************/
658
659         private void decrement (Ref p)
660         {
661                 Reap (p.value);
662                 heap.collect (p);
663                 ++mutation;
664                 --count;
665         }
666        
667         /***********************************************************************
668
669                 set was changed
670                 
671         ***********************************************************************/
672
673         private void mutate()
674         {
675                 ++mutation;
676         }
677
678         /***********************************************************************
679
680                 Iterator with no filtering
681
682         ***********************************************************************/
683
684         private struct Iterator
685         {
686                 uint    row;
687                 Ref     cell,
688                         prior;
689                 Ref[]   table;
690                 HashSet owner;
691                 uint    mutation;
692
693                 /***************************************************************
694
695                         Did the container change underneath us?
696
697                 ***************************************************************/
698
699                 bool valid ()
700                 {
701                         return owner.mutation is mutation;
702                 }               
703
704                 /***************************************************************
705
706                         Accesses the next value, and returns false when
707                    &