1 /* 2 * Hunt - A refined core library for D programming language. 3 * 4 * Copyright (C) 2018-2019 HuntLabs 5 * 6 * Website: https://www.huntlabs.net/ 7 * 8 * Licensed under the Apache-2.0 License. 9 * 10 */ 11 12 module hunt.collection.HashSet; 13 14 import hunt.collection.AbstractSet; 15 import hunt.collection.Collection; 16 import hunt.collection.HashMap; 17 import hunt.collection.LinkedHashMap; 18 import hunt.collection.Set; 19 20 import hunt.Object; 21 22 import std.algorithm; 23 import std.range; 24 25 /** 26 * This class implements the <tt>Set</tt> interface, backed by a hash table 27 * (actually a <tt>HashMap</tt> instance). It makes no guarantees as to the 28 * iteration order of the set; in particular, it does not guarantee that the 29 * order will remain constant over time. This class permits the <tt>null</tt> 30 * element. 31 * 32 * <p>This class offers constant time performance for the basic operations 33 * (<tt>add</tt>, <tt>remove</tt>, <tt>contains</tt> and <tt>size</tt>), 34 * assuming the hash function disperses the elements properly among the 35 * buckets. Iterating over this set requires time proportional to the sum of 36 * the <tt>HashSet</tt> instance's size (the number of elements) plus the 37 * "capacity" of the backing <tt>HashMap</tt> instance (the number of 38 * buckets). Thus, it's very important not to set the initial capacity too 39 * high (or the load factor too low) if iteration performance is important. 40 * 41 * <p><strong>Note that this implementation is not synchronized.</strong> 42 * If multiple threads access a hash set concurrently, and at least one of 43 * the threads modifies the set, it <i>must</i> be synchronized externally. 44 * This is typically accomplished by synchronizing on some object that 45 * naturally encapsulates the set. 46 * 47 * If no such object exists, the set should be "wrapped" using the 48 * {@link Collections#synchronizedSet Collections.synchronizedSet} 49 * method. This is best done at creation time, to prevent accidental 50 * unsynchronized access to the set:<pre> 51 * Set s = Collections.synchronizedSet(new HashSet(...));</pre> 52 * 53 * <p>The iterators returned by this class's <tt>iterator</tt> method are 54 * <i>fail-fast</i>: if the set is modified at any time after the iterator is 55 * created, in any way except through the iterator's own <tt>remove</tt> 56 * method, the Iterator throws a {@link ConcurrentModificationException}. 57 * Thus, in the face of concurrent modification, the iterator fails quickly 58 * and cleanly, rather than risking arbitrary, non-deterministic behavior at 59 * an undetermined time in the future. 60 * 61 * <p>Note that the fail-fast behavior of an iterator cannot be guaranteed 62 * as it is, generally speaking, impossible to make any hard guarantees in the 63 * presence of unsynchronized concurrent modification. Fail-fast iterators 64 * throw <tt>ConcurrentModificationException</tt> on a best-effort basis. 65 * Therefore, it would be wrong to write a program that depended on this 66 * exception for its correctness: <i>the fail-fast behavior of iterators 67 * should be used only to detect bugs.</i> 68 * 69 * <p>This class is a member of the 70 * <a href="{@docRoot}/../technotes/guides/collections/index.html"> 71 * Java Collections Framework</a>. 72 * 73 * @param <E> the type of elements maintained by this set 74 * 75 * @author Josh Bloch 76 * @author Neal Gafter 77 * @see Collection 78 * @see Set 79 * @see TreeSet 80 * @see HashMap 81 * @since 1.2 82 */ 83 class HashSet(E) : AbstractSet!E, Set!E { 84 85 protected HashMap!(E, Object) map; 86 87 // Dummy value to associate with an Object in the backing Map 88 private __gshared static Object PRESENT; // = new Object(); 89 90 shared static this() { 91 PRESENT = new Object(); 92 } 93 94 /** 95 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has 96 * default initial capacity (16) and load factor (0.75). 97 */ 98 this() { 99 map = new HashMap!(E, Object)(); 100 } 101 102 /** 103 * Constructs a new set containing the elements in the specified 104 * collection. The <tt>HashMap</tt> is created with default load factor 105 * (0.75) and an initial capacity sufficient to contain the elements in 106 * the specified collection. 107 * 108 * @param c the collection whose elements are to be placed into this set 109 * @throws NullPointerException if the specified collection is null 110 */ 111 this(Collection!E c) { 112 map = new HashMap!(E, Object)(std.algorithm.max(cast(int)(c.size() / .75f) + 1, 16)); 113 addAll(c); 114 } 115 116 this(E[] c) { 117 map = new HashMap!(E, Object)(std.algorithm.max(cast(int)(c.length / .75f) + 1, 16)); 118 addAll(c); 119 } 120 121 /** 122 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has 123 * the specified initial capacity and the specified load factor. 124 * 125 * @param initialCapacity the initial capacity of the hash map 126 * @param loadFactor the load factor of the hash map 127 * @throws IllegalArgumentException if the initial capacity is less 128 * than zero, or if the load factor is nonpositive 129 */ 130 this(int initialCapacity, float loadFactor) { 131 map = new HashMap!(E, Object)(initialCapacity, loadFactor); 132 } 133 134 /** 135 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has 136 * the specified initial capacity and default load factor (0.75). 137 * 138 * @param initialCapacity the initial capacity of the hash table 139 * @throws IllegalArgumentException if the initial capacity is less 140 * than zero 141 */ 142 this(int initialCapacity) { 143 map = new HashMap!(E, Object)(initialCapacity); 144 } 145 146 /** 147 * Constructs a new, empty linked hash set. (This package private 148 * constructor is only used by LinkedHashSet.) The backing 149 * HashMap instance is a LinkedHashMap with the specified initial 150 * capacity and the specified load factor. 151 * 152 * @param initialCapacity the initial capacity of the hash map 153 * @param loadFactor the load factor of the hash map 154 * @param dummy ignored (distinguishes this 155 * constructor from other int, float constructor.) 156 * @throws IllegalArgumentException if the initial capacity is less 157 * than zero, or if the load factor is nonpositive 158 */ 159 this(int initialCapacity, float loadFactor, bool dummy) { 160 map = new LinkedHashMap!(E, Object)(initialCapacity, loadFactor); 161 } 162 163 /** 164 * Returns an iterator over the elements in this set. The elements 165 * are returned in no particular order. 166 * 167 * @return an Iterator over the elements in this set 168 * @see ConcurrentModificationException 169 */ 170 override InputRange!E iterator() { 171 return map.byKey; 172 } 173 174 /** 175 * Returns the number of elements in this set (its cardinality). 176 * 177 * @return the number of elements in this set (its cardinality) 178 */ 179 override int size() { 180 return map.size(); 181 } 182 183 /** 184 * Returns <tt>true</tt> if this set contains no elements. 185 * 186 * @return <tt>true</tt> if this set contains no elements 187 */ 188 override bool isEmpty() { 189 return map.isEmpty(); 190 } 191 192 /** 193 * Returns <tt>true</tt> if this set contains the specified element. 194 * More formally, returns <tt>true</tt> if and only if this set 195 * contains an element <tt>e</tt> such that 196 * <tt>(o is null ? e is null : o.equals(e))</tt>. 197 * 198 * @param o element whose presence in this set is to be tested 199 * @return <tt>true</tt> if this set contains the specified element 200 */ 201 override bool contains(E o) { 202 return map.containsKey(o); 203 } 204 205 /** 206 * Adds the specified element to this set if it is not already present. 207 * More formally, adds the specified element <tt>e</tt> to this set if 208 * this set contains no element <tt>e2</tt> such that 209 * <tt>(e is null ? e2 is null : e.equals(e2))</tt>. 210 * If this set already contains the element, the call leaves the set 211 * unchanged and returns <tt>false</tt>. 212 * 213 * @param e element to be added to this set 214 * @return <tt>true</tt> if this set did not already contain the specified 215 * element 216 */ 217 override bool add(E e) { 218 return map.put(e, PRESENT) is null; 219 } 220 221 /** 222 * Removes the specified element from this set if it is present. 223 * More formally, removes an element <tt>e</tt> such that 224 * <tt>(o is null ? e is null : o.equals(e))</tt>, 225 * if this set contains such an element. Returns <tt>true</tt> if 226 * this set contained the element (or equivalently, if this set 227 * changed as a result of the call). (This set will not contain the 228 * element once the call returns.) 229 * 230 * @param o object to be removed from this set, if present 231 * @return <tt>true</tt> if the set contained the specified element 232 */ 233 override bool remove(E o) { 234 return map.remove(o) == PRESENT; 235 } 236 237 /** 238 * Removes all of the elements from this set. 239 * The set will be empty after this call returns. 240 */ 241 override void clear() { 242 map.clear(); 243 } 244 245 /** 246 * Returns a shallow copy of this <tt>HashSet</tt> instance: the elements 247 * themselves are not cloned. 248 * 249 * @return a shallow copy of this set 250 */ 251 // 252 // Object clone() { 253 // try { 254 // HashSet<E> newSet = (HashSet<E>) super.clone(); 255 // newSet.map = (HashMap<E, Object>) map.clone(); 256 // return newSet; 257 // } catch (CloneNotSupportedException e) { 258 // throw new InternalError(e); 259 // } 260 // } 261 262 /** 263 * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em> 264 * and <em>fail-fast</em> {@link Spliterator} over the elements in this 265 * set. 266 * 267 * <p>The {@code Spliterator} reports {@link Spliterator#SIZED} and 268 * {@link Spliterator#DISTINCT}. Overriding implementations should document 269 * the reporting of additional characteristic values. 270 * 271 * @return a {@code Spliterator} over the elements in this set 272 * @since 1.8 273 */ 274 // Spliterator<E> spliterator() { 275 // return new HashMap.KeySpliterator!(E, Object)(map, 0, -1, 0, 0); 276 // } 277 278 override int opApply(scope int delegate(ref E) dg) { 279 int result = 0; 280 foreach (E v; map.byKey) { 281 result = dg(v); 282 if (result != 0) 283 return result; 284 } 285 return result; 286 } 287 288 override bool opEquals(IObject o) { 289 return opEquals(cast(Object) o); 290 } 291 292 override bool opEquals(Object o) { 293 return super.opEquals(o); 294 } 295 296 override size_t toHash() @trusted nothrow { 297 return super.toHash(); 298 } 299 300 override string toString() { 301 return super.toString(); 302 } 303 }