1 /*
2  * SimpleHashSet.java
3  * This file is part of JaCoP.
4  * <p>
5  * JaCoP is a Java Constraint Programming solver.
6  * <p>
7  * Copyright (C) 2000-2008 Krzysztof Kuchcinski and Radoslaw Szymanek
8  * <p>
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  * <p>
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  * <p>
19  * Notwithstanding any other provision of this License, the copyright
20  * owners of this work supplement the terms of this License with terms
21  * prohibiting misrepresentation of the origin of this work and requiring
22  * that modified versions of this work be marked in reasonable ways as
23  * different from the original version. This supplement of the license
24  * terms is in accordance with Section 7 of GNU Affero General Public
25  * License version 3.
26  * <p>
27  * You should have received a copy of the GNU Affero General Public License
28  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
29  */
30 
31 package org.jacop.util;
32 
33 import java.util.Arrays;
34 
35 /**
36  * This class provides very simple HashSet functionality. Designed specially for
37  * maintaining pending constraints for evaluation. It's implementation was
38  * partially based on standard hash set implementation as implemented in java
39  * util class.
40  *
41  * @param <E> Class being stored in SimpleHashSet.
42  * @author Radoslaw Szymanek and Krzysztof Kuchcinski
43  * @version 4.8
44  */
45 
46 public class SimpleHashSet<E> {
47 
48     @SuppressWarnings("hiding") class Entry<E> {
49 
50         @SuppressWarnings("unchecked") public Entry chain;
51 
52         public final E element;
53 
54         @SuppressWarnings("unchecked") public Entry next;
55 
56         /**
57          * Create new entry.
58          */
Entry(E el)59         Entry(E el) {
60             element = el;
61             next = null;
62         }
63 
64         /**
65          * Create new entry.
66          */
Entry(E el, Entry<E> n)67         Entry(E el, Entry<E> n) {
68             element = el;
69             next = n;
70         }
71 
add(E addedElement)72         @SuppressWarnings("unchecked") public boolean add(E addedElement) {
73             if (element == addedElement)
74                 return false;
75             if (next != null)
76                 return next.add(addedElement);
77             else {
78                 next = new Entry(addedElement);
79                 lastEntry.chain = next;
80                 lastEntry = next;
81                 return true;
82             }
83         }
84 
contains(E checkedElement)85         @SuppressWarnings("unchecked") public boolean contains(E checkedElement) {
86             if (element == checkedElement)
87                 return true;
88             if (next != null)
89                 return next.contains(checkedElement);
90             else {
91                 return false;
92             }
93         }
94 
95     }
96 
97 
98     /**
99      * The default initial capacity - MUST be a power of two.
100      */
101     static final int DEFAULT_INITIAL_CAPACITY = 16;
102 
103     /**
104      * The load factor used when none specified in constructor.
105      */
106     static final float DEFAULT_LOAD_FACTOR = 0.75f;
107 
108     /**
109      * The maximum capacity, used if a higher value is implicitly specified by
110      * either of the constructors with arguments. MUST be a power of two <= 1<<30.
111      */
112     static final int MAXIMUM_CAPACITY = 1 << 30;
113 
114     /**
115      * Returns a hash value for the specified object. In addition to the
116      * object's own hashCode, this method applies a "supplemental hash
117      * function," which defends against poor quality hash functions. This is
118      * critical because SimpleHashSet uses power-of two length hash tables.
119      * <p>
120      * <p>
121      * The shift distances in this function were chosen as the result of an
122      * automated search over the entire four-dimensional search space.
123      * <p>
124      * This hash code function implementation is original Sun function proposed
125      * in util package.
126      */
127 
hash(Object x)128     static int hash(Object x) {
129         int h = x.hashCode();
130 
131         h += ~(h << 9);
132         h ^= (h >>> 14);
133         h += (h << 4);
134         h ^= (h >>> 10);
135         return h;
136     }
137 
138     /**
139      * Returns index for hash code h.
140      */
indexFor(int h, int length)141     static int indexFor(int h, int length) {
142         return h & (length - 1);
143     }
144 
145     /**
146      * It points to the first Entry to be removed.
147      */
148     transient Entry<E> firstEntry = null;
149 
150     /**
151      * The initial capacity for the hash set.
152      */
153     int initialCapacity;
154 
155     /**
156      * It points to the last Entry being add.
157      */
158     transient Entry<E> lastEntry = null;
159 
160     /**
161      * The load factor for the hash set.
162      */
163     final float loadFactor;
164 
165     /**
166      * The number of elements contained in this set.
167      */
168     transient int size;
169 
170     /**
171      * The set, resized as necessary. Length MUST Always be a power of two.
172      */
173     @SuppressWarnings("unchecked") private transient Entry[] table;
174 
175     /**
176      * The next size value at which to resize (capacity * load factor).
177      */
178     int threshold;
179 
180     /**
181      * Constructs an empty <tt>HashSet</tt> with the default initial capacity
182      * (16) and the default load factor (0.75).
183      */
SimpleHashSet()184     public SimpleHashSet() {
185         this.loadFactor = DEFAULT_LOAD_FACTOR;
186         threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
187         table = new Entry[DEFAULT_INITIAL_CAPACITY];
188         initialCapacity = table.length;
189     }
190 
191     /**
192      * Constructs an empty <tt>HashSet</tt> with the specified initial
193      * capacity and the default load factor (0.75).
194      *
195      * @param initialCapacity the initial capacity.
196      * @throws IllegalArgumentException if the initial capacity is negative.
197      */
SimpleHashSet(int initialCapacity)198     public SimpleHashSet(int initialCapacity) {
199         this(initialCapacity, DEFAULT_LOAD_FACTOR);
200     }
201 
202     /**
203      * Constructs an empty <tt>HashSet</tt> with the specified initial
204      * capacity and load factor.
205      *
206      * @param initialCapacity The initial capacity.
207      * @param loadFactor      The load factor.
208      * @throws IllegalArgumentException if the initial capacity is negative or the load factor is
209      *                                  nonpositive.
210      */
SimpleHashSet(int initialCapacity, float loadFactor)211     public SimpleHashSet(int initialCapacity, float loadFactor) {
212         if (initialCapacity < 0)
213             throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity);
214         if (initialCapacity > MAXIMUM_CAPACITY)
215             initialCapacity = MAXIMUM_CAPACITY;
216         if (loadFactor <= 0 || Float.isNaN(loadFactor))
217             throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
218 
219         // Find a power of 2 >= initialCapacity
220         int capacity = 1;
221         while (capacity < initialCapacity)
222             capacity <<= 1;
223 
224         this.loadFactor = loadFactor;
225         this.threshold = (int) (capacity * loadFactor);
226         this.table = new Entry[capacity];
227         this.initialCapacity = table.length;
228     }
229 
230     /**
231      * Adds the specified element to this set.
232      *
233      * @param element element with which the specified value is to be associated.
234      * @return <tt>true</tt> if object is inserted and <tt>false</tt> if
235      * object was already in the set.
236      */
add(E element)237     @SuppressWarnings("unchecked") public boolean add(E element) {
238         int hash = hash(element);
239         int i = indexFor(hash, table.length);
240 
241         Entry<E> e = table[i];
242 
243         if (e != null) {
244             boolean result = e.add(element);
245 
246             if (result) {
247                 // checks threshold and increases size
248                 if (size++ >= threshold)
249                     resize(2 * table.length);
250             }
251             return result;
252         } else {
253             e = table[i] = new Entry<E>(element);
254 
255             if (firstEntry == null) {
256                 firstEntry = e;
257                 lastEntry = firstEntry;
258             } else {
259                 lastEntry.chain = e;
260                 lastEntry = e;
261             }
262 
263             // checks threshold and increases size
264             if (size++ >= threshold)
265                 resize(2 * table.length);
266         }
267 
268         return true;
269     }
270 
271     /**
272      * Removes all elements from this set.
273      */
clear()274     public void clear() {
275 
276         //	Entry<E>[] tab = table;
277         //	for (int i = tab.length - 1; i >= 0; i--)
278         //		tab[i] = null;
279 
280         Arrays.fill(table, null);
281 
282         firstEntry = null;
283         lastEntry = null;
284 
285         size = 0;
286     }
287 
288     /**
289      * Clones this set.
290      */
291 
clone()292     @Override @SuppressWarnings("unchecked") public Object clone() {
293         SimpleHashSet<E> result = new SimpleHashSet<E>();
294 
295         result.table = new Entry[table.length];
296         result.size = size;
297         result.initialCapacity = result.table.length;
298 
299         for (int i = table.length - 1; i >= 0; i--) {
300             Entry<E> e = table[i];
301             if (e != null) {
302                 result.table[i] = new Entry<E>(e.element);
303                 e = e.next;
304             }
305 
306             while (e != null) {
307                 result.table[i].add(e.element);
308                 e = e.next;
309             }
310         }
311 
312         return result;
313     }
314 
315     /**
316      * Returns the boolean value which specifies if given element is already in
317      * this identity hash set.
318      *
319      * @param element the element whose existence in the hash set is to be checked.
320      * @return the boolean value which specifies if given element exists in a
321      * hash set.
322      */
contains(E element)323     @SuppressWarnings({"unchecked"}) public boolean contains(E element) {
324         int hash = hash(element);
325         int i = indexFor(hash, table.length);
326         Entry<E> e = table[i];
327         if (e != null)
328             return e.contains(element);
329         else
330             return false;
331     }
332 
333     /**
334      * Returns <tt>true</tt> if this set contains no elements.
335      *
336      * @return <tt>true</tt> if this set contains no elements.
337      */
isEmpty()338     public boolean isEmpty() {
339         return size == 0;
340     }
341 
342     /**
343      * Removes and returns an entry removed from the HashSet. Returns null if
344      * the HashSet contains no entry.
345      *
346      * @return the first entry which has been removed.
347      */
348 
removeFirst()349     @SuppressWarnings("unchecked") public E removeFirst() {
350 
351         if (size == 0)
352             return null;
353 
354         Entry<E> removed = firstEntry;
355 
356         firstEntry = firstEntry.chain;
357 
358         size--;
359 
360         int hash = hash(removed.element);
361         int i = indexFor(hash, table.length);
362 
363         if (removed.next == null)
364             table[i] = null;
365         else
366             table[i] = removed.next;
367 
368         return removed.element;
369 
370     }
371 
372     /**
373      * Rehashes the contents of this set into a new array with a larger
374      * capacity. This method is called automatically when the number of elements
375      * in this set reaches its threshold.
376      * <p>
377      * If current capacity is MAXIMUM_CAPACITY, this method does not resize the
378      * set, but sets threshold to Integer.MAX_VALUE. This has the effect of
379      * preventing future calls.
380      *
381      * @param newCapacity the new capacity, MUST be a power of two; must be greater than
382      *                    current capacity unless current capacity is MAXIMUM_CAPACITY
383      *                    (in which case value is irrelevant).
384      */
resize(int newCapacity)385     @SuppressWarnings("unchecked") void resize(int newCapacity) {
386 
387         Entry[] oldTable = table;
388         int oldCapacity = oldTable.length;
389         if (oldCapacity == MAXIMUM_CAPACITY) {
390             threshold = Integer.MAX_VALUE;
391             return;
392         }
393 
394         table = new Entry[newCapacity];
395         threshold = (int) (newCapacity * loadFactor);
396         transfer(oldTable);
397 
398     }
399 
400     /**
401      * Returns the number of elements in this set.
402      *
403      * @return the number of elements in this set.
404      */
size()405     public int size() {
406         return size;
407     }
408 
409     /**
410      * Returns string representation of the hash set.
411      */
412 
toString()413     @Override @SuppressWarnings("unchecked") public String toString() {
414         StringBuffer S = new StringBuffer();
415 
416         S.append("SimpleHashSet[");
417 
418         Entry[] tab = table;
419 
420         for (int i = 0; i < tab.length; i++) {
421 
422             Entry<E> e = tab[i];
423 
424             while (e != null) {
425                 S.append(e.element);
426                 e = e.next;
427                 if (e != null)
428                     S.append(",");
429             }
430 
431         }
432 
433         S.append("]");
434 
435         return S.toString();
436     }
437 
438     /**
439      * Transfer all entries from current table to newTable.
440      */
transfer(Entry[] oldTable)441     @SuppressWarnings("unchecked") void transfer(Entry[] oldTable) {
442 
443         size = 0;
444 
445         Entry<E> temp = firstEntry;
446         firstEntry = null;
447 
448         while (temp != null) {
449             add(temp.element);
450             temp = temp.chain;
451         }
452 
453     }
454 
455 }
456