2 Copyright (c) 2003-2006 Niels Kokholm and Peter Sestoft
\r
3 Permission is hereby granted, free of charge, to any person obtaining a copy
\r
4 of this software and associated documentation files (the "Software"), to deal
\r
5 in the Software without restriction, including without limitation the rights
\r
6 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
\r
7 copies of the Software, and to permit persons to whom the Software is
\r
8 furnished to do so, subject to the following conditions:
\r
10 The above copyright notice and this permission notice shall be included in
\r
11 all copies or substantial portions of the Software.
\r
13 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
\r
14 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
\r
15 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
\r
16 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
\r
17 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
\r
18 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
\r
22 #define LINEARPROBINGnot
\r
25 #define INTERHASHINGnot
\r
26 #define RANDOMINTERHASHING
\r
29 using System.Diagnostics;
\r
30 using SCG = System.Collections.Generic;
\r
35 /// A set collection class based on linear hashing
\r
38 public class HashSet<T> : CollectionBase<T>, ICollection<T>
\r
42 /// Enum class to assist printing of compilation alternatives.
\r
45 public enum Feature : short
\r
52 /// Buckets are of reference type
\r
56 /// Primary buckets are of value type
\r
58 ValueTypeBucket = 2,
\r
60 /// Using linear probing to resolve index clashes
\r
64 /// Shrink table when very sparsely filled
\r
68 /// Use chaining to resolve index clashes
\r
72 /// Use hash function on item hash code
\r
76 /// Use a universal family of hash functions on item hash code
\r
78 RandomInterHashing = 64
\r
83 static Feature features = Feature.Dummy
\r
85 | Feature.RefTypeBucket
\r
87 | Feature.ValueTypeBucket
\r
90 | Feature.ShrinkTable
\r
93 | Feature.LinearProbing
\r
98 | Feature.InterHashing
\r
99 #elif RANDOMINTERHASHING
\r
100 | Feature.RandomInterHashing
\r
106 /// Show which implementation features was chosen at compilation time
\r
108 public static Feature Features { get { return features; } }
\r
114 int indexmask, bits, bitsc, origbits, lastchosen; //bitsc==32-bits; indexmask==(1<<bits)-1;
\r
119 bool defaultvalid = false;
\r
123 double fillfactor = 0.66;
\r
125 int resizethreshhold;
\r
127 #if RANDOMINTERHASHING
\r
129 const uint randomhashfactor = 1529784659;
\r
131 uint randomhashfactor = (2 * (uint)(new Random()).Next() + 1) * 1529784659;
\r
142 /// <value></value>
\r
143 public override EventTypeEnum ListenableEvents { get { return EventTypeEnum.Basic; } }
\r
147 #region Bucket nested class(es)
\r
154 internal int hashval; //Cache!
\r
157 internal Bucket(T item, int hashval)
\r
160 this.hashval = hashval;
\r
163 internal Bucket overflow;
\r
165 internal Bucket(T item, int hashval, Bucket overflow)
\r
168 this.hashval = hashval;
\r
169 this.overflow = overflow;
\r
178 internal int hashval; //Cache!
\r
181 internal Bucket(T item, int hashval)
\r
184 this.hashval = hashval;
\r
187 internal OverflowBucket overflow;
\r
190 internal Bucket(T item, int hashval)
\r
193 this.hashval = hashval;
\r
194 this.overflow = default(OverflowBucket);
\r
201 class OverflowBucket
\r
205 internal int hashval; //Cache!
\r
207 internal OverflowBucket overflow;
\r
210 internal OverflowBucket(T item, int hashval, OverflowBucket overflow)
\r
213 this.hashval = hashval;
\r
214 this.overflow = overflow;
\r
224 bool equals(T i1, T i2) { return itemequalityComparer.Equals(i1, i2); }
\r
227 bool isnull(T item) { return itemequalityComparer.Equals(item, default(T)); }
\r
230 int gethashcode(T item) { return itemequalityComparer.GetHashCode(item); }
\r
233 int hv2i(int hashval)
\r
236 //Note: *inverse mod 2^32 is -1503427877
\r
237 return (int)(((uint)hashval * 1529784659) >>bitsc);
\r
238 #elif RANDOMINTERHASHING
\r
239 return (int)(((uint)hashval * randomhashfactor) >> bitsc);
\r
241 return indexmask & hashval;
\r
248 //Console.WriteLine(String.Format("Expand to {0} bits", bits+1));
\r
257 //Console.WriteLine(String.Format("Shrink to {0} bits", bits - 1));
\r
263 void resize(int bits)
\r
265 //Console.WriteLine(String.Format("Resize to {0} bits", bits));
\r
268 indexmask = (1 << bits) - 1;
\r
270 Bucket[] newtable = new Bucket[indexmask + 1];
\r
272 for (int i = 0, s = table.Length; i < s; i++)
\r
274 Bucket b = table[i];
\r
280 int j = hv2i(b.hashval);
\r
282 while (newtable[j] != null) { j = indexmask & (j + 1); }
\r
287 if (!isnull(b.item))
\r
289 int j = hv2i(b.hashval);
\r
291 while (!isnull(newtable[j].item)) { j = indexmask & (j + 1); }
\r
300 int j = hv2i(b.hashval);
\r
302 newtable[j] = new Bucket(b.item, b.hashval, newtable[j]);
\r
306 if (!isnull(b.item))
\r
308 insert(b.item, b.hashval, newtable);
\r
310 OverflowBucket ob = b.overflow;
\r
314 insert(ob.item, ob.hashval, newtable);
\r
323 resizethreshhold = (int)(table.Length * fillfactor);
\r
324 //Console.WriteLine(String.Format("Resize to {0} bits done", bits));
\r
331 //Only for resize!!!
\r
332 private void insert(T item, int hashval, Bucket[] t)
\r
334 int i = hv2i(hashval);
\r
337 if (!isnull(b.item))
\r
339 t[i].overflow = new OverflowBucket(item, hashval, b.overflow);
\r
342 t[i] = new Bucket(item, hashval);
\r
348 /// Search for an item equal (according to itemequalityComparer) to the supplied item.
\r
350 /// <param name="item"></param>
\r
351 /// <param name="add">If true, add item to table if not found.</param>
\r
352 /// <param name="update">If true, update table entry if item found.</param>
\r
353 /// <param name="raise">If true raise events</param>
\r
354 /// <returns>True if found</returns>
\r
355 private bool searchoradd(ref T item, bool add, bool update, bool raise)
\r
360 int hashval = gethashcode(item);
\r
361 int i = hv2i(hashval);
\r
362 Bucket b = table[i];
\r
366 T olditem = b.item;
\r
367 if (equals(olditem, item))
\r
374 if (raise && update)
\r
375 raiseForUpdate(item, olditem);
\r
379 b = table[i = indexmask & (i + 1)];
\r
382 if (!add) goto notfound;
\r
384 table[i] = new Bucket(item, hashval);
\r
391 T olditem = defaultitem;
\r
393 defaultitem = item;
\r
395 item = defaultitem;
\r
397 if (raise && update)
\r
398 raiseForUpdate(item, olditem);
\r
402 if (!add) goto notfound;
\r
404 defaultvalid = true;
\r
405 defaultitem = item;
\r
409 int hashval = gethashcode(item);
\r
410 int i = hv2i(hashval);
\r
411 T t = table[i].item;
\r
415 if (equals(t, item))
\r
418 table[i].item = item;
\r
422 if (raise && update)
\r
423 raiseForUpdate(item, t);
\r
427 t = table[i = indexmask & (i + 1)].item;
\r
430 if (!add) goto notfound;
\r
432 table[i] = new Bucket(item, hashval);
\r
437 int hashval = gethashcode(item);
\r
438 int i = hv2i(hashval);
\r
439 Bucket b = table[i], bold = null;
\r
445 T olditem = b.item;
\r
446 if (equals(olditem, item))
\r
454 if (raise && update)
\r
455 raiseForUpdate(item, olditem);
\r
463 if (!add) goto notfound;
\r
465 bold.overflow = new Bucket(item, hashval, null);
\r
469 if (!add) goto notfound;
\r
471 table[i] = new Bucket(item, hashval, null);
\r
478 T olditem = defaultitem;
\r
480 defaultitem = item;
\r
482 item = defaultitem;
\r
484 if (raise && update)
\r
485 raiseForUpdate(item, olditem);
\r
489 if (!add) goto notfound;
\r
491 defaultvalid = true;
\r
492 defaultitem = item;
\r
496 int hashval = gethashcode(item);
\r
497 int i = hv2i(hashval);
\r
498 Bucket b = table[i];
\r
500 if (!isnull(b.item))
\r
502 if (equals(b.item, item))
\r
505 table[i].item = item;
\r
509 if (raise && update)
\r
510 raiseForUpdate(item, b.item);
\r
514 OverflowBucket ob = table[i].overflow;
\r
518 if (!add) goto notfound;
\r
520 table[i].overflow = new OverflowBucket(item, hashval, null);
\r
524 T olditem = ob.item;
\r
525 while (ob.overflow != null)
\r
527 if (equals(item, olditem))
\r
534 if (raise && update)
\r
535 raiseForUpdate(item, olditem);
\r
543 if (equals(item, olditem))
\r
550 if (raise && update)
\r
551 raiseForUpdate(item, olditem);
\r
555 if (!add) goto notfound;
\r
557 ob.overflow = new OverflowBucket(item, hashval, null);
\r
562 if (!add) goto notfound;
\r
564 table[i] = new Bucket(item, hashval);
\r
570 if (size > resizethreshhold)
\r
579 private bool remove(ref T item)
\r
586 int hashval = gethashcode(item);
\r
587 int index = hv2i(hashval);
\r
588 Bucket b = table[index];
\r
592 if (equals(item, b.item))
\r
595 item = table[index].item;
\r
596 table[index] = null;
\r
599 int j = (index + 1) & indexmask;
\r
604 int k = hv2i(b.hashval);
\r
606 if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index))
\r
607 //if (index > j ? (j < k && k <= index): (k <= index || j < k) )
\r
614 j = (j + 1) & indexmask;
\r
621 b = table[index = indexmask & (index + 1)];
\r
631 item = defaultitem;
\r
632 defaultvalid = false;
\r
633 defaultitem = default(T); //No spaceleaks!
\r
637 int hashval = gethashcode(item);
\r
638 int index = hv2i(hashval);
\r
639 T t = table[index].item;
\r
643 if (equals(item, t))
\r
646 item = table[index].item;
\r
647 table[index].item = default(T);
\r
650 int j = (index + 1) & indexmask;
\r
651 Bucket b = table[j];
\r
653 while (!isnull(b.item))
\r
655 int k = hv2i(b.hashval);
\r
657 if ((k <= index && index < j) || (index < j && j < k) || (j < k && k <= index))
\r
660 table[j].item = default(T);
\r
664 j = (j + 1) & indexmask;
\r
671 t = table[index = indexmask & (index + 1)].item;
\r
680 int hashval = gethashcode(item);
\r
681 int index = hv2i(hashval);
\r
682 Bucket b = table[index], bold;
\r
687 if (equals(item, b.item))
\r
691 table[index] = b.overflow;
\r
697 while (b != null && !equals(item, b.item))
\r
708 bold.overflow = b.overflow;
\r
718 item = defaultitem;
\r
719 defaultvalid = false;
\r
720 defaultitem = default(T); //No spaceleaks!
\r
724 int hashval = gethashcode(item);
\r
725 int index = hv2i(hashval);
\r
726 Bucket b = table[index];
\r
727 OverflowBucket ob = b.overflow;
\r
729 if (equals(item, b.item))
\r
735 table[index] = new Bucket();
\r
739 b = new Bucket(ob.item, ob.hashval);
\r
740 b.overflow = ob.overflow;
\r
749 if (equals(item, ob.item))
\r
753 table[index].overflow = ob.overflow;
\r
757 while (ob.overflow != null)
\r
758 if (equals(item, ob.overflow.item))
\r
761 item = ob.overflow.item;
\r
767 if (ob.overflow == null)
\r
770 ob.overflow = ob.overflow.overflow;
\r
782 private void clear()
\r
786 indexmask = (1 << bits) - 1;
\r
788 table = new Bucket[indexmask + 1];
\r
789 resizethreshhold = (int)(table.Length * fillfactor);
\r
791 defaultitem = default(T);
\r
792 defaultvalid = false;
\r
798 #region Constructors
\r
800 /// Create a hash set with natural item equalityComparer and default fill threshold (66%)
\r
801 /// and initial table size (16).
\r
804 : this(EqualityComparer<T>.Default) { }
\r
808 /// Create a hash set with external item equalityComparer and default fill threshold (66%)
\r
809 /// and initial table size (16).
\r
811 /// <param name="itemequalityComparer">The external item equalityComparer</param>
\r
812 public HashSet(SCG.IEqualityComparer<T> itemequalityComparer)
\r
813 : this(16, itemequalityComparer) { }
\r
817 /// Create a hash set with external item equalityComparer and default fill threshold (66%)
\r
819 /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param>
\r
820 /// <param name="itemequalityComparer">The external item equalityComparer</param>
\r
821 public HashSet(int capacity, SCG.IEqualityComparer<T> itemequalityComparer)
\r
822 : this(capacity, 0.66, itemequalityComparer) { }
\r
826 /// Create a hash set with external item equalityComparer.
\r
828 /// <param name="capacity">Initial table size (rounded to power of 2, at least 16)</param>
\r
829 /// <param name="fill">Fill threshold (in range 10% to 90%)</param>
\r
830 /// <param name="itemequalityComparer">The external item equalityComparer</param>
\r
831 public HashSet(int capacity, double fill, SCG.IEqualityComparer<T> itemequalityComparer) : base(itemequalityComparer)
\r
833 if (fill < 0.1 || fill > 0.9)
\r
834 throw new ArgumentException("Fill outside valid range [0.1, 0.9]");
\r
836 throw new ArgumentException("Capacity must be non-negative");
\r
837 //this.itemequalityComparer = itemequalityComparer;
\r
839 while (capacity - 1 >> origbits > 0) origbits++;
\r
847 #region IEditableCollection<T> Members
\r
850 /// The complexity of the Contains operation
\r
852 /// <value>Always returns Speed.Constant</value>
\r
854 public virtual Speed ContainsSpeed { [Tested]get { return Speed.Constant; } }
\r
857 /// Check if an item is in the set
\r
859 /// <param name="item">The item to look for</param>
\r
860 /// <returns>True if set contains item</returns>
\r
862 public virtual bool Contains(T item) { return searchoradd(ref item, false, false, false); }
\r
866 /// Check if an item (collection equal to a given one) is in the set and
\r
867 /// if so report the actual item object found.
\r
869 /// <param name="item">On entry, the item to look for.
\r
870 /// On exit the item found, if any</param>
\r
871 /// <returns>True if set contains item</returns>
\r
873 public virtual bool Find(ref T item) { return searchoradd(ref item, false, false, false); }
\r
877 /// Check if an item (collection equal to a given one) is in the set and
\r
878 /// if so replace the item object in the set with the supplied one.
\r
880 /// <param name="item">The item object to update with</param>
\r
881 /// <returns>True if item was found (and updated)</returns>
\r
883 public virtual bool Update(T item)
\r
884 { updatecheck(); return searchoradd(ref item, false, true, true); }
\r
887 /// Check if an item (collection equal to a given one) is in the set and
\r
888 /// if so replace the item object in the set with the supplied one.
\r
890 /// <param name="item">The item object to update with</param>
\r
891 /// <param name="olditem"></param>
\r
892 /// <returns>True if item was found (and updated)</returns>
\r
893 public virtual bool Update(T item, out T olditem)
\r
894 { updatecheck(); olditem = item; return searchoradd(ref olditem, false, true, true); }
\r
898 /// Check if an item (collection equal to a given one) is in the set.
\r
899 /// If found, report the actual item object in the set,
\r
900 /// else add the supplied one.
\r
902 /// <param name="item">On entry, the item to look for or add.
\r
903 /// On exit the actual object found, if any.</param>
\r
904 /// <returns>True if item was found</returns>
\r
906 public virtual bool FindOrAdd(ref T item)
\r
907 { updatecheck(); return searchoradd(ref item, true, false, true); }
\r
911 /// Check if an item (collection equal to a supplied one) is in the set and
\r
912 /// if so replace the item object in the set with the supplied one; else
\r
913 /// add the supplied one.
\r
915 /// <param name="item">The item to look for and update or add</param>
\r
916 /// <returns>True if item was updated</returns>
\r
918 public virtual bool UpdateOrAdd(T item)
\r
919 { updatecheck(); return searchoradd(ref item, true, true, true); }
\r
923 /// Check if an item (collection equal to a supplied one) is in the set and
\r
924 /// if so replace the item object in the set with the supplied one; else
\r
925 /// add the supplied one.
\r
927 /// <param name="item">The item to look for and update or add</param>
\r
928 /// <param name="olditem"></param>
\r
929 /// <returns>True if item was updated</returns>
\r
930 public virtual bool UpdateOrAdd(T item, out T olditem)
\r
931 { updatecheck(); olditem = item; return searchoradd(ref olditem, true, true, true); }
\r
935 /// Remove an item from the set
\r
937 /// <param name="item">The item to remove</param>
\r
938 /// <returns>True if item was (found and) removed </returns>
\r
940 public virtual bool Remove(T item)
\r
943 if (remove(ref item))
\r
946 if (size<resizethreshhold/2 && resizethreshhold>8)
\r
949 raiseForRemove(item);
\r
958 /// Remove an item from the set, reporting the actual matching item object.
\r
960 /// <param name="item">The value to remove.</param>
\r
961 /// <param name="removeditem">The removed value.</param>
\r
962 /// <returns>True if item was found.</returns>
\r
964 public virtual bool Remove(T item, out T removeditem)
\r
967 removeditem = item;
\r
968 if (remove(ref removeditem))
\r
971 if (size<resizethreshhold/2 && resizethreshhold>8)
\r
974 raiseForRemove(removeditem);
\r
983 /// Remove all items in a supplied collection from this set.
\r
985 /// <typeparam name="U"></typeparam>
\r
986 /// <param name="items">The items to remove.</param>
\r
988 public virtual void RemoveAll<U>(SCG.IEnumerable<U> items) where U : T
\r
991 RaiseForRemoveAllHandler raiseHandler = new RaiseForRemoveAllHandler(this);
\r
992 bool raise = raiseHandler.MustFire;
\r
994 foreach (U item in items)
\r
995 { jtem = item; if (remove(ref jtem) && raise) raiseHandler.Remove(jtem); }
\r
997 if (size < resizethreshhold / 2 && resizethreshhold > 16)
\r
999 int newlength = table.Length;
\r
1001 while (newlength >= 32 && newlength * fillfactor / 2 > size)
\r
1004 resize(newlength - 1);
\r
1007 if (raise) raiseHandler.Raise();
\r
1011 /// Remove all items from the set, resetting internal table to initial size.
\r
1014 public virtual void Clear()
\r
1017 int oldsize = size;
\r
1019 if (ActiveEvents != 0 && oldsize > 0)
\r
1021 raiseCollectionCleared(true, oldsize);
\r
1022 raiseCollectionChanged();
\r
1028 /// Remove all items *not* in a supplied collection from this set.
\r
1030 /// <typeparam name="U"></typeparam>
\r
1031 /// <param name="items">The items to retain</param>
\r
1033 public virtual void RetainAll<U>(SCG.IEnumerable<U> items) where U : T
\r
1037 HashSet<T> aux = new HashSet<T>(EqualityComparer);
\r
1039 //This only works for sets:
\r
1040 foreach (U item in items)
\r
1041 if (Contains(item))
\r
1045 aux.searchoradd(ref jtem, true, false, false);
\r
1048 if (size == aux.size)
\r
1051 CircularQueue<T> wasRemoved = null;
\r
1052 if ((ActiveEvents & EventTypeEnum.Removed) != 0)
\r
1054 wasRemoved = new CircularQueue<T>();
\r
1055 foreach (T item in this)
\r
1056 if (!aux.Contains(item))
\r
1057 wasRemoved.Enqueue(item);
\r
1060 table = aux.table;
\r
1063 defaultvalid = aux.defaultvalid;
\r
1064 defaultitem = aux.defaultitem;
\r
1066 indexmask = aux.indexmask;
\r
1067 resizethreshhold = aux.resizethreshhold;
\r
1070 if ((ActiveEvents & EventTypeEnum.Removed) != 0)
\r
1071 raiseForRemoveAll(wasRemoved);
\r
1072 else if ((ActiveEvents & EventTypeEnum.Changed) != 0)
\r
1073 raiseCollectionChanged();
\r
1077 /// Check if all items in a supplied collection is in this set
\r
1078 /// (ignoring multiplicities).
\r
1080 /// <param name="items">The items to look for.</param>
\r
1081 /// <typeparam name="U"></typeparam>
\r
1082 /// <returns>True if all items are found.</returns>
\r
1084 public virtual bool ContainsAll<U>(SCG.IEnumerable<U> items) where U : T
\r
1086 foreach (U item in items)
\r
1087 if (!Contains(item))
\r
1094 /// Create an array containing all items in this set (in enumeration order).
\r
1096 /// <returns>The array</returns>
\r
1098 public override T[] ToArray()
\r
1100 T[] res = new T[size];
\r
1105 res[index++] = defaultitem;
\r
1107 for (int i = 0; i < table.Length; i++)
\r
1109 Bucket b = table[i];
\r
1113 res[index++] = b.item;
\r
1115 if (!isnull(b.item))
\r
1116 res[index++] = b.item;
\r
1122 res[index++] = b.item;
\r
1126 if (!isnull(b.item))
\r
1128 res[index++] = b.item;
\r
1130 OverflowBucket ob = b.overflow;
\r
1132 while (ob != null)
\r
1134 res[index++] = ob.item;
\r
1142 Debug.Assert(size == index);
\r
1148 /// Count the number of times an item is in this set (either 0 or 1).
\r
1150 /// <param name="item">The item to look for.</param>
\r
1151 /// <returns>1 if item is in set, 0 else</returns>
\r
1153 public virtual int ContainsCount(T item) { return Contains(item) ? 1 : 0; }
\r
1158 /// <returns></returns>
\r
1159 public virtual ICollectionValue<T> UniqueItems() { return this; }
\r
1164 /// <returns></returns>
\r
1165 public virtual ICollectionValue<KeyValuePair<T, int>> ItemMultiplicities()
\r
1167 return new MultiplicityOne<T>(this);
\r
1171 /// Remove all (at most 1) copies of item from this set.
\r
1173 /// <param name="item">The item to remove</param>
\r
1175 public virtual void RemoveAllCopies(T item) { Remove(item); }
\r
1179 #region IEnumerable<T> Members
\r
1183 /// Choose some item of this collection.
\r
1185 /// <exception cref="NoSuchItemException">if collection is empty.</exception>
\r
1186 /// <returns></returns>
\r
1188 public override T Choose()
\r
1190 int len = table.Length;
\r
1192 throw new NoSuchItemException();
\r
1194 do { if (++lastchosen >= len) lastchosen = 0; } while (table[lastchosen] == null);
\r
1196 if (defaultvalid) return defaultitem;
\r
1197 do { if (++lastchosen >= len) lastchosen = 0; } while (isnull(table[lastchosen].item));
\r
1199 return table[lastchosen].item;
\r
1203 /// Create an enumerator for this set.
\r
1205 /// <returns>The enumerator</returns>
\r
1207 public override SCG.IEnumerator<T> GetEnumerator()
\r
1210 int mystamp = stamp;
\r
1211 int len = table.Length;
\r
1215 while (++index < len)
\r
1217 if (mystamp != stamp) throw new CollectionModifiedException();
\r
1219 if (table[index] != null) yield return table[index].item;
\r
1223 yield return defaultitem;
\r
1225 while (++index < len)
\r
1227 if (mystamp != stamp) throw new CollectionModifiedException();
\r
1229 T item = table[index].item;
\r
1231 if (!isnull(item)) yield return item;
\r
1238 OverflowBucket ob = null;
\r
1241 yield return defaultitem;
\r
1245 if (mystamp != stamp)
\r
1246 throw new CollectionModifiedException();
\r
1249 if (b == null || b.overflow == null)
\r
1253 if (++index >= len) yield break;
\r
1254 } while (table[index] == null);
\r
1257 yield return b.item;
\r
1262 yield return b.item;
\r
1265 if (ob != null && ob.overflow != null)
\r
1268 yield return ob.item;
\r
1270 else if (index >= 0 && ob == null && (ob = table[index].overflow) != null)
\r
1272 yield return ob.item;
\r
1278 if (++index >= len) yield break;
\r
1279 } while (isnull(table[index].item));
\r
1281 yield return table[index].item;
\r
1291 #region ISink<T> Members
\r
1293 /// Report if this is a set collection.
\r
1295 /// <value>Always false</value>
\r
1297 public virtual bool AllowsDuplicates { [Tested]get { return false; } }
\r
1300 /// By convention this is true for any collection with set semantics.
\r
1302 /// <value>True if only one representative of a group of equal items
\r
1303 /// is kept in the collection together with the total count.</value>
\r
1304 public virtual bool DuplicatesByCounting { get { return true; } }
\r
1307 /// Add an item to this set.
\r
1309 /// <param name="item">The item to add.</param>
\r
1310 /// <returns>True if item was added (i.e. not found)</returns>
\r
1312 public virtual bool Add(T item)
\r
1315 return !searchoradd(ref item, true, false, true);
\r
1319 /// Add the elements from another collection with a more specialized item type
\r
1320 /// to this collection. Since this
\r
1321 /// collection has set semantics, only items not already in the collection
\r
1322 /// will be added.
\r
1324 /// <typeparam name="U">The type of items to add</typeparam>
\r
1325 /// <param name="items">The items to add</param>
\r
1327 public virtual void AddAll<U>(SCG.IEnumerable<U> items) where U : T
\r
1330 bool wasChanged = false;
\r
1331 bool raiseAdded = (ActiveEvents & EventTypeEnum.Added) != 0;
\r
1332 CircularQueue<T> wasAdded = raiseAdded ? new CircularQueue<T>() : null;
\r
1333 foreach (T item in items)
\r
1337 if (!searchoradd(ref jtem, true, false, false))
\r
1339 wasChanged = true;
\r
1341 wasAdded.Enqueue(item);
\r
1344 //TODO: implement a RaiseForAddAll() method
\r
1345 if (raiseAdded & wasChanged)
\r
1346 foreach (T item in wasAdded)
\r
1347 raiseItemsAdded(item, 1);
\r
1348 if (((ActiveEvents & EventTypeEnum.Changed) != 0 && wasChanged))
\r
1349 raiseCollectionChanged();
\r
1355 #region Diagnostics
\r
1358 /// Test internal structure of data (invariants)
\r
1360 /// <returns>True if pass</returns>
\r
1362 public virtual bool Check()
\r
1366 int lasthole = table.Length - 1;
\r
1369 while (lasthole >= 0 && table[lasthole] != null)
\r
1371 while (lasthole >= 0 && !isnull(table[lasthole].item))
\r
1380 Console.WriteLine("Table is completely filled!");
\r
1384 for (int cellindex = lasthole + 1, s = table.Length; cellindex < s; cellindex++)
\r
1386 Bucket b = table[cellindex];
\r
1387 int hashindex = hv2i(b.hashval);
\r
1389 if (hashindex <= lasthole || hashindex > cellindex)
\r
1391 Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, lasthole={4}", b.item, b.hashval, hashindex, cellindex, lasthole);
\r
1396 int latesthole = -1;
\r
1398 for (int cellindex = 0; cellindex < lasthole; cellindex++)
\r
1400 Bucket b = table[cellindex];
\r
1405 if (!isnull(b.item))
\r
1410 int hashindex = hv2i(b.hashval);
\r
1412 if (cellindex < hashindex && hashindex <= lasthole)
\r
1414 Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole);
\r
1420 latesthole = cellindex;
\r
1425 for (int cellindex = latesthole + 1; cellindex < lasthole; cellindex++)
\r
1427 Bucket b = table[cellindex];
\r
1432 if (!isnull(b.item))
\r
1437 int hashindex = hv2i(b.hashval);
\r
1439 if (hashindex <= latesthole || cellindex < hashindex)
\r
1441 Console.WriteLine("Bad cell item={0}, hashval={1}, hashindex={2}, cellindex={3}, latesthole={4}", b.item, b.hashval, hashindex, cellindex, latesthole);
\r
1447 latesthole = cellindex;
\r
1453 bool retval = true;
\r
1454 for (int i = 0, s = table.Length; i < s; i++)
\r
1457 Bucket b = table[i];
\r
1461 if (i != hv2i(b.hashval))
\r
1463 Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
\r
1472 if (!isnull(b.item))
\r
1475 if (i != hv2i(b.hashval))
\r
1477 Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
\r
1481 OverflowBucket ob = b.overflow;
\r
1483 while (ob != null)
\r
1487 if (i != hv2i(ob.hashval))
\r
1489 Console.WriteLine("Bad cell item={0}, hashval={1}, index={2}, level={3}", b.item, b.hashval, i, level);
\r
1499 if (count != size)
\r
1501 Console.WriteLine("size({0}) != count({1})", size, count);
\r
1511 /// Produce statistics on distribution of bucket sizes. Current implementation is incomplete.
\r
1513 /// <returns>Histogram data.</returns>
\r
1514 [Tested(via = "Manually")]
\r
1515 public ISortedDictionary<int, int> BucketCostDistribution()
\r
1517 TreeDictionary<int, int> res = new TreeDictionary<int, int>();
\r
1521 while (table[count] != null)
\r
1523 while (!isnull(table[count].item))
\r
1526 for (int i = table.Length - 1; i >= 0; i--)
\r
1529 if (table[i] != null)
\r
1531 if (!isnull(table[i].item))
\r
1536 if (res.Contains(count))
\r
1544 for (int i = 0, s = table.Length; i < s; i++)
\r
1548 Bucket b = table[i];
\r
1556 Bucket b = table[i];
\r
1558 if (!isnull(b.item))
\r
1562 OverflowBucket ob = b.overflow;
\r
1564 while (ob != null)
\r
1571 if (res.Contains(count))
\r
1583 #region ICloneable Members
\r
1586 /// Make a shallow copy of this HashSet.
\r
1588 /// <returns></returns>
\r
1589 public virtual object Clone()
\r
1591 HashSet<T> clone = new HashSet<T>(size > 0 ? size : 1, itemequalityComparer);
\r
1592 //TODO: make sure this really adds in the counting bag way!
\r
1593 clone.AddAll(this);
\r