package gnu.trove.set.hash; import gnu.trove.iterator.TIntIterator; import gnu.trove.iterator.hash.TObjectHashIterator; import gnu.trove.list.TIntList; import gnu.trove.list.array.TIntArrayList; import gnu.trove.list.linked.TIntLinkedList; import gnu.trove.procedure.TIntProcedure; import gnu.trove.procedure.TObjectProcedure; import java.io.IOException; import java.io.ObjectOutput; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; /** * Created by IntelliJ IDEA. * User: Johan * Date: 15/03/11 * Time: 18:15 * To change this template use File | Settings | File Templates. */ public class TLinkedHashSet<E> extends THashSet<E> { TIntList order; /** * Creates a new <code>THashSet</code> instance with the default * capacity and load factor. */ public TLinkedHashSet() { } /** * Creates a new <code>THashSet</code> instance with a prime * capacity equal to or greater than <tt>initialCapacity</tt> and * with the default load factor. * * @param initialCapacity an <code>int</code> value */ public TLinkedHashSet(int initialCapacity) { super(initialCapacity); } /** * Creates a new <code>THashSet</code> instance with a prime * capacity equal to or greater than <tt>initialCapacity</tt> and * with the specified load factor. * * @param initialCapacity an <code>int</code> value * @param loadFactor a <code>float</code> value */ public TLinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); } /** * Creates a new <code>THashSet</code> instance containing the * elements of <tt>collection</tt>. * * @param es a <code>Collection</code> value */ public TLinkedHashSet(Collection<? extends E> es) { super(es); } /** * initializes the Object set of this hash table. * * @param initialCapacity an <code>int</code> value * @return an <code>int</code> value */ @Override public int setUp(int initialCapacity) { order = new TIntArrayList(initialCapacity) { /** * Grow the internal array as needed to accommodate the specified number of elements. * The size of the array bytes on each resize unless capacity requires more than twice * the current capacity. */ @Override public void ensureCapacity(int capacity) { if (capacity > _data.length) { int newCap = Math.max(_set.length, capacity); int[] tmp = new int[newCap]; System.arraycopy(_data, 0, tmp, 0, _data.length); _data = tmp; } } }; return super.setUp(initialCapacity); //To change body of overridden methods use File | Settings | File Templates. } /** * Empties the set. */ @Override public void clear() { super.clear(); order.clear(); } @Override public String toString() { final StringBuilder buf = new StringBuilder("{"); boolean first = true; for (Iterator<E> it = iterator(); it.hasNext();) { if (first) { first = false; } else { buf.append(", "); } buf.append(it.next()); } buf.append("}"); return buf.toString(); } /** * Inserts a value into the set. * * @param obj an <code>Object</code> value * @return true if the set was modified by the add operation */ @Override public boolean add(E obj) { int index = insertKey(obj); if (index < 0) { return false; // already present in set, nothing to add } if (!order.add(index)) throw new IllegalStateException("Order not changed after insert"); postInsertHook(consumeFreeSlot); return true; // yes, we added something } @Override protected void removeAt(int index) { // Remove from order first since super.removeAt can trigger compaction // making the index invalid afterwards order.remove(index); super.removeAt(index); } /** * Expands the set to accommodate new values. * * @param newCapacity an <code>int</code> value */ @Override protected void rehash(int newCapacity) { TIntLinkedList oldOrder = new TIntLinkedList(order); int oldSize = size(); Object oldSet[] = _set; order.clear(); _set = new Object[newCapacity]; Arrays.fill(_set, FREE); for (TIntIterator iterator = oldOrder.iterator(); iterator.hasNext();) { int i = iterator.next(); E o = (E) oldSet[i]; if (o == FREE || o == REMOVED) { throw new IllegalStateException("Iterating over empty location while rehashing"); } if (o != FREE && o != REMOVED) { int index = insertKey(o); if (index < 0) { // everyone pays for this because some people can't RTFM throwObjectContractViolation(_set[(-index - 1)], o, size(), oldSize, oldSet); } if (!order.add(index)) throw new IllegalStateException("Order not changed after insert"); } } } class WriteProcedure implements TIntProcedure { final ObjectOutput output; IOException ioException; WriteProcedure(ObjectOutput output) { this.output = output; } public IOException getIoException() { return ioException; } public boolean execute(int value) { try { output.writeObject(_set[value]); } catch (IOException e) { ioException = e; return false; } return true; } } @Override protected void writeEntries(ObjectOutput out) throws IOException { // ENTRIES WriteProcedure writeProcedure = new WriteProcedure(out); if (!order.forEach(writeProcedure)) throw writeProcedure.getIoException(); } /** * Creates an iterator over the values of the set. The iterator * supports element deletion. * * @return an <code>Iterator</code> value */ @Override public TObjectHashIterator<E> iterator() { return new TObjectHashIterator<E>(this) { TIntIterator localIterator = order.iterator(); int lastIndex; /** * Moves the iterator to the next Object and returns it. * * @return an <code>Object</code> value * @throws java.util.ConcurrentModificationException * if the structure * was changed using a method that isn't on this iterator. * @throws java.util.NoSuchElementException * if this is called on an * exhausted iterator. */ @Override public E next() { lastIndex = localIterator.next(); return objectAtIndex(lastIndex); } /** * Returns true if the iterator can be advanced past its current * location. * * @return a <code>boolean</code> value */ @Override public boolean hasNext() { return localIterator.hasNext(); //To change body of overridden methods use File | Settings | File Templates. } /** * Removes the last entry returned by the iterator. * Invoking this method more than once for a single entry * will leave the underlying data structure in a confused * state. */ @Override public void remove() { // Remove for iterator first localIterator.remove(); // the removal within removeAt() will not change the collection // but the localIterator will remain valid try { _hash.tempDisableAutoCompaction(); TLinkedHashSet.this.removeAt(lastIndex); } finally { _hash.reenableAutoCompaction(false); } } }; //To change body of overridden methods use File | Settings | File Templates. } class ForEachProcedure implements TIntProcedure { boolean changed = false; final Object[] set; final TObjectProcedure<? super E> procedure; public ForEachProcedure(Object[] set, TObjectProcedure<? super E> procedure) { this.set = set; this.procedure = procedure; } /** * Executes this procedure. A false return value indicates that * the application executing this procedure should not invoke this * procedure again. * * @param value a value of type <code>int</code> * @return true if additional invocations of the procedure are * allowed. */ public boolean execute(int value) { return procedure.execute((E) set[value]); } } /** * Executes <tt>procedure</tt> for each element in the set. * * @param procedure a <code>TObjectProcedure</code> value * @return false if the loop over the set terminated because * the procedure returned false for some value. */ @Override public boolean forEach(TObjectProcedure<? super E> procedure) { ForEachProcedure forEachProcedure = new ForEachProcedure(_set, procedure); return order.forEach(forEachProcedure); } }