///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001, Eric D. Friedman All Rights Reserved.
// Copyright (c) 2009, Rob Eden All Rights Reserved.
// Copyright (c) 2009, Jeff Randall All Rights Reserved.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
///////////////////////////////////////////////////////////////////////////////
package gnu.trove.set.hash;
import gnu.trove.strategy.HashingStrategy;
import gnu.trove.impl.HashFunctions;
import gnu.trove.impl.hash.TCustomObjectHash;
import gnu.trove.iterator.hash.TObjectHashIterator;
import gnu.trove.procedure.TObjectProcedure;
import gnu.trove.procedure.array.ToObjectArrayProceedure;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
/**
* An implementation of the <tt>Set</tt> interface that uses an
* open-addressed hash table to store its contents.
*
* @author Rob Eden
*/
public class TCustomHashSet<E> extends TCustomObjectHash<E>
implements Set<E>, Iterable<E>, Externalizable {
static final long serialVersionUID = 1L;
/** FOR EXTERNALIZATION ONLY!!! */
public TCustomHashSet() {}
/**
* Creates a new <code>THashSet</code> instance with the default
* capacity and load factor.
*/
public TCustomHashSet( HashingStrategy<E> strategy ) {
super( strategy );
}
/**
* 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 TCustomHashSet( HashingStrategy<E> strategy, int initialCapacity ) {
super( strategy, 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 TCustomHashSet( HashingStrategy<E> strategy, int initialCapacity,
float loadFactor ) {
super( strategy, initialCapacity, loadFactor );
}
/**
* Creates a new <code>THashSet</code> instance containing the
* elements of <tt>collection</tt>.
*
* @param collection a <code>Collection</code> value
*/
public TCustomHashSet( HashingStrategy<E> strategy,
Collection<? extends E> collection ) {
this( strategy, collection.size() );
addAll( collection );
}
/**
* Inserts a value into the set.
*
* @param obj an <code>Object</code> value
* @return true if the set was modified by the add operation
*/
public boolean add( E obj ) {
int index = insertionIndex( obj );
if ( index < 0 ) {
return false; // already present in set, nothing to add
}
Object old = _set[index];
_set[index] = obj;
postInsertHook( old == FREE );
return true; // yes, we added something
}
@SuppressWarnings({"SimplifiableIfStatement"})
public boolean equals( Object other ) {
if ( !( other instanceof Set ) ) {
return false;
}
Set that = (Set) other;
if ( that.size() != this.size() ) {
return false;
}
return containsAll( that );
}
public int hashCode() {
HashProcedure p = new HashProcedure();
forEach( p );
return p.getHashCode();
}
private final class HashProcedure implements TObjectProcedure<E> {
private int h = 0;
public int getHashCode() {
return h;
}
public final boolean execute( E key ) {
h += HashFunctions.hash( key );
return true;
}
}
/**
* Expands the set to accommodate new values.
*
* @param newCapacity an <code>int</code> value
*/
@SuppressWarnings({"unchecked"})
protected void rehash( int newCapacity ) {
int oldCapacity = _set.length;
Object oldSet[] = _set;
_set = new Object[newCapacity];
Arrays.fill( _set, FREE );
for ( int i = oldCapacity; i-- > 0; ) {
if ( oldSet[i] != FREE && oldSet[i] != REMOVED ) {
E o = (E) oldSet[i];
int index = insertionIndex( o );
if ( index < 0 ) { // everyone pays for this because some people can't RTFM
throwObjectContractViolation( _set[( -index - 1 )], o );
}
_set[index] = o;
}
}
}
/**
* Returns a new array containing the objects in the set.
*
* @return an <code>Object[]</code> value
*/
@SuppressWarnings({"unchecked"})
public Object[] toArray() {
Object[] result = new Object[size()];
forEach( new ToObjectArrayProceedure( result ) );
return result;
}
/**
* Returns a typed array of the objects in the set.
*
* @param a an <code>Object[]</code> value
* @return an <code>Object[]</code> value
*/
@SuppressWarnings({"unchecked"})
public <T> T[] toArray( T[] a ) {
int size = size();
if ( a.length < size ) {
a = (T[]) Array.newInstance( a.getClass().getComponentType(), size );
}
forEach( new ToObjectArrayProceedure( a ) );
// If this collection fits in the specified array with room to
// spare (i.e., the array has more elements than this
// collection), the element in the array immediately following
// the end of the collection is set to null. This is useful in
// determining the length of this collection only if the
// caller knows that this collection does not contain any null
// elements.)
if ( a.length > size ) {
a[size] = null;
}
return a;
}
/** Empties the set. */
public void clear() {
super.clear();
Arrays.fill( _set, 0, _set.length, FREE );
}
/**
* Removes <tt>obj</tt> from the set.
*
* @param obj an <code>Object</code> value
* @return true if the set was modified by the remove operation.
*/
@SuppressWarnings({"unchecked"})
public boolean remove( Object obj ) {
int index = index( obj );
if ( index >= 0 ) {
removeAt( index );
return true;
}
return false;
}
/**
* Creates an iterator over the values of the set. The iterator
* supports element deletion.
*
* @return an <code>Iterator</code> value
*/
@SuppressWarnings({"unchecked"})
public TObjectHashIterator<E> iterator() {
return new TObjectHashIterator<E>( this );
}
/**
* Tests the set to determine if all of the elements in
* <tt>collection</tt> are present.
*
* @param collection a <code>Collection</code> value
* @return true if all elements were present in the set.
*/
@SuppressWarnings("ForLoopReplaceableByForEach")
public boolean containsAll( Collection<?> collection ) {
for ( Iterator i = collection.iterator(); i.hasNext(); ) {
if ( !contains( i.next() ) ) {
return false;
}
}
return true;
}
/**
* Adds all of the elements in <tt>collection</tt> to the set.
*
* @param collection a <code>Collection</code> value
* @return true if the set was modified by the add all operation.
*/
public boolean addAll( Collection<? extends E> collection ) {
boolean changed = false;
int size = collection.size();
ensureCapacity( size );
Iterator<? extends E> it = collection.iterator();
while ( size-- > 0 ) {
if ( add( it.next() ) ) {
changed = true;
}
}
return changed;
}
/**
* Removes all of the elements in <tt>collection</tt> from the set.
*
* @param collection a <code>Collection</code> value
* @return true if the set was modified by the remove all operation.
*/
public boolean removeAll( Collection<?> collection ) {
boolean changed = false;
int size = collection.size();
Iterator it;
it = collection.iterator();
while ( size-- > 0 ) {
if ( remove( it.next() ) ) {
changed = true;
}
}
return changed;
}
/**
* Removes any values in the set which are not contained in
* <tt>collection</tt>.
*
* @param collection a <code>Collection</code> value
* @return true if the set was modified by the retain all operation
*/
@SuppressWarnings({"SuspiciousMethodCalls"})
public boolean retainAll( Collection<?> collection ) {
boolean changed = false;
int size = size();
Iterator<E> it = iterator();
while ( size-- > 0 ) {
if ( !collection.contains( it.next() ) ) {
it.remove();
changed = true;
}
}
return changed;
}
public String toString() {
final StringBuilder buf = new StringBuilder( "{" );
forEach( new TObjectProcedure<E>() {
private boolean first = true;
public boolean execute( Object value ) {
if ( first ) {
first = false;
} else {
buf.append( ", " );
}
buf.append( value );
return true;
}
} );
buf.append( "}" );
return buf.toString();
}
public void writeExternal( ObjectOutput out ) throws IOException {
// VERSION
out.writeByte( 1 );
// NOTE: Super was not written in version 0
super.writeExternal( out );
// NUMBER OF ENTRIES
out.writeInt( _size );
// ENTRIES
for ( int i = _set.length; i-- > 0; ) {
if ( _set[i] != REMOVED && _set[i] != FREE ) {
out.writeObject( _set[i] );
}
}
}
@SuppressWarnings({"unchecked"})
public void readExternal( ObjectInput in )
throws IOException, ClassNotFoundException {
// VERSION
byte version = in.readByte();
// NOTE: super was not written in version 0
if ( version != 0 ) {
super.readExternal( in );
}
// NUMBER OF ENTRIES
int size = in.readInt();
setUp( size );
// ENTRIES
while ( size-- > 0 ) {
E val = (E) in.readObject();
add( val );
}
}
}