gnu.trove.TObjectHash Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of trove4j Show documentation
Show all versions of trove4j Show documentation
The Trove library provides high speed regular and primitive
collections for Java.
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2001, Eric D. Friedman 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;
import java.util.Arrays;
/**
* An open addressed hashing implementation for Object types.
*
* Created: Sun Nov 4 08:56:06 2001
*
* @author Eric D. Friedman
* @version $Id: TObjectHash.java,v 1.25 2007/03/30 20:53:22 robeden Exp $
*/
abstract public class TObjectHash extends THash implements TObjectHashingStrategy {
static final long serialVersionUID = -3461112548087185871L;
/** the set of Objects */
protected transient Object[] _set;
/** the strategy used to hash objects in this collection. */
protected TObjectHashingStrategy _hashingStrategy;
protected static final Object REMOVED = new Object(), FREE = new Object();
/**
* Creates a new TObjectHash
instance with the
* default capacity and load factor.
*/
public TObjectHash() {
super();
this._hashingStrategy = this;
}
/**
* Creates a new TObjectHash
instance with the
* default capacity and load factor and a custom hashing strategy.
*
* @param strategy used to compute hash codes and to compare objects.
*/
public TObjectHash(TObjectHashingStrategy strategy) {
super();
this._hashingStrategy = strategy;
}
/**
* Creates a new TObjectHash
instance whose capacity
* is the next highest prime above initialCapacity + 1
* unless that value is already prime.
*
* @param initialCapacity an int
value
*/
public TObjectHash(int initialCapacity) {
super(initialCapacity);
this._hashingStrategy = this;
}
/**
* Creates a new TObjectHash
instance whose capacity
* is the next highest prime above initialCapacity + 1
* unless that value is already prime. Uses the specified custom
* hashing strategy.
*
* @param initialCapacity an int
value
* @param strategy used to compute hash codes and to compare objects.
*/
public TObjectHash(int initialCapacity, TObjectHashingStrategy strategy) {
super(initialCapacity);
this._hashingStrategy = strategy;
}
/**
* Creates a new TObjectHash
instance with a prime
* value at or near the specified capacity and load factor.
*
* @param initialCapacity used to find a prime capacity for the table.
* @param loadFactor used to calculate the threshold over which
* rehashing takes place.
*/
public TObjectHash(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
this._hashingStrategy = this;
}
/**
* Creates a new TObjectHash
instance with a prime
* value at or near the specified capacity and load factor. Uses
* the specified custom hashing strategy.
*
* @param initialCapacity used to find a prime capacity for the table.
* @param loadFactor used to calculate the threshold over which
* rehashing takes place.
* @param strategy used to compute hash codes and to compare objects.
*/
public TObjectHash(int initialCapacity, float loadFactor, TObjectHashingStrategy strategy) {
super(initialCapacity, loadFactor);
this._hashingStrategy = strategy;
}
/**
* @return a shallow clone of this collection
*/
public TObjectHash clone() {
TObjectHash h = (TObjectHash)super.clone();
h._set = (Object[])this._set.clone();
return h;
}
protected int capacity() {
return _set.length;
}
protected void removeAt(int index) {
_set[index] = REMOVED;
super.removeAt(index);
}
/**
* initializes the Object set of this hash table.
*
* @param initialCapacity an int
value
* @return an int
value
*/
protected int setUp(int initialCapacity) {
int capacity;
capacity = super.setUp(initialCapacity);
_set = new Object[capacity];
Arrays.fill(_set,FREE);
return capacity;
}
/**
* Executes procedure for each element in the set.
*
* @param procedure a TObjectProcedure
value
* @return false if the loop over the set terminated because
* the procedure returned false for some value.
*/
public boolean forEach(TObjectProcedure procedure) {
Object[] set = _set;
for (int i = set.length; i-- > 0;) {
if (set[i] != FREE
&& set[i] != REMOVED
&& ! procedure.execute((T) set[i])) {
return false;
}
}
return true;
}
/**
* Searches the set for obj
*
* @param obj an Object
value
* @return a boolean
value
*/
public boolean contains(Object obj) {
return index((T) obj) >= 0;
}
/**
* Locates the index of obj.
*
* @param obj an Object
value
* @return the index of obj or -1 if it isn't in the set.
*/
protected int index(T obj) {
final TObjectHashingStrategy hashing_strategy = _hashingStrategy;
final Object[] set = _set;
final int length = set.length;
final int hash = hashing_strategy.computeHashCode(obj) & 0x7fffffff;
int index = hash % length;
Object cur = set[index];
if ( cur == FREE ) return -1;
// NOTE: here it has to be REMOVED or FULL (some user-given value)
if ( cur == REMOVED || ! hashing_strategy.equals((T) cur, obj)) {
// see Knuth, p. 529
final int probe = 1 + (hash % (length - 2));
do {
index -= probe;
if (index < 0) {
index += length;
}
cur = set[index];
} while (cur != FREE
&& (cur == REMOVED || ! _hashingStrategy.equals((T) cur, obj)));
}
return cur == FREE ? -1 : index;
}
/**
* Locates the index at which obj can be inserted. if
* there is already a value equal()ing obj in the set,
* returns that value's index as -index - 1.
*
* @param obj an Object
value
* @return the index of a FREE slot at which obj can be inserted
* or, if obj is already stored in the hash, the negative value of
* that index, minus 1: -index -1.
*/
protected int insertionIndex(T obj) {
final TObjectHashingStrategy hashing_strategy = _hashingStrategy;
final Object[] set = _set;
final int length = set.length;
final int hash = hashing_strategy.computeHashCode(obj) & 0x7fffffff;
int index = hash % length;
Object cur = set[index];
if (cur == FREE) {
return index; // empty, all done
} else if (cur != REMOVED && hashing_strategy.equals((T) cur, obj)) {
return -index -1; // already stored
} else { // already FULL or REMOVED, must probe
// compute the double hash
final int probe = 1 + (hash % (length - 2));
// if the slot we landed on is FULL (but not removed), probe
// until we find an empty slot, a REMOVED slot, or an element
// equal to the one we are trying to insert.
// finding an empty slot means that the value is not present
// and that we should use that slot as the insertion point;
// finding a REMOVED slot means that we need to keep searching,
// however we want to remember the offset of that REMOVED slot
// so we can reuse it in case a "new" insertion (i.e. not an update)
// is possible.
// finding a matching value means that we've found that our desired
// key is already in the table
if (cur != REMOVED) {
// starting at the natural offset, probe until we find an
// offset that isn't full.
do {
index -= probe;
if (index < 0) {
index += length;
}
cur = set[index];
} while (cur != FREE
&& cur != REMOVED
&& ! hashing_strategy.equals((T) cur, obj));
}
// if the index we found was removed: continue probing until we
// locate a free location or an element which equal()s the
// one we have.
if (cur == REMOVED) {
int firstRemoved = index;
while (cur != FREE
&& (cur == REMOVED || ! hashing_strategy.equals((T) cur, obj))) {
index -= probe;
if (index < 0) {
index += length;
}
cur = set[index];
}
// NOTE: cur cannot == REMOVED in this block
return (cur != FREE) ? -index -1 : firstRemoved;
}
// if it's full, the key is already stored
// NOTE: cur cannot equal REMOVE here (would have retuned already (see above)
return (cur != FREE) ? -index -1 : index;
}
}
/**
* This is the default implementation of TObjectHashingStrategy:
* it delegates hashing to the Object's hashCode method.
*
* @param o for which the hashcode is to be computed
* @return the hashCode
* @see Object#hashCode()
*/
public final int computeHashCode(T o) {
return o == null ? 0 : o.hashCode();
}
/**
* This is the default implementation of TObjectHashingStrategy:
* it delegates equality comparisons to the first parameter's
* equals() method.
*
* @param o1 an Object
value
* @param o2 an Object
value
* @return true if the objects are equal
* @see Object#equals(Object)
*/
public final boolean equals(T o1, T o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
/**
* Convenience methods for subclasses to use in throwing exceptions about
* badly behaved user objects employed as keys. We have to throw an
* IllegalArgumentException with a rather verbose message telling the
* user that they need to fix their object implementation to conform
* to the general contract for java.lang.Object.
*
* @param o1 the first of the equal elements with unequal hash codes.
* @param o2 the second of the equal elements with unequal hash codes.
* @exception IllegalArgumentException the whole point of this method.
*/
protected final void throwObjectContractViolation(Object o1, Object o2)
throws IllegalArgumentException {
throw new IllegalArgumentException("Equal objects must have equal hashcodes. "
+ "During rehashing, Trove discovered that "
+ "the following two objects claim to be "
+ "equal (as in java.lang.Object.equals()) "
+ "but their hashCodes (or those calculated by "
+ "your TObjectHashingStrategy) are not equal."
+ "This violates the general contract of "
+ "java.lang.Object.hashCode(). See bullet point two "
+ "in that method's documentation. "
+ "object #1 =" + o1
+ "; object #2 =" + o2);
}
} // TObjectHash