com.carrotsearch.hppcrt.sets.ObjectOpenIdentityHashSet Maven / Gradle / Ivy
Show all versions of hppcrt Show documentation
package com.carrotsearch.hppcrt.sets;
import com.carrotsearch.hppcrt.*;
import com.carrotsearch.hppcrt.cursors.*;
import com.carrotsearch.hppcrt.predicates.*;
import com.carrotsearch.hppcrt.procedures.*;
import com.carrotsearch.hppcrt.hash.*;
/**
* An identity hash set of KType
types, implemented using open
* addressing with linear probing for collision resolution.
*
* The difference with {@link ObjectOpenHashSet} is that it uses direct Object reference equality for comparison and
* direct "address" {@link System#identityHashCode(Object)} for hashCode(), instead of using
* the built-in hashCode() / equals().
*
* The internal buffers of this implementation ({@link #keys}, etc...)
* are always allocated to the nearest size that is a power of two. When
* the capacity exceeds the given load factor, the buffer size is doubled.
*
*
* This implementation supports null
keys.
*
* @author This code is inspired by the collaboration and implementation in the fastutil project.
*
*
*/
@javax.annotation.Generated(date = "2015-02-27T19:21:18+0100", value = "HPPC-RT generated from: ObjectOpenIdentityHashSet.java")
public class ObjectOpenIdentityHashSet
extends AbstractObjectCollection
implements ObjectLookupContainer, ObjectSet, Cloneable
{
/**
* Minimum capacity for the map.
*/
public final static int MIN_CAPACITY = HashContainerUtils.MIN_CAPACITY;
/**
* Default capacity.
*/
public final static int DEFAULT_CAPACITY = HashContainerUtils.DEFAULT_CAPACITY;
/**
* Default load factor.
*/
public final static float DEFAULT_LOAD_FACTOR = HashContainerUtils.DEFAULT_LOAD_FACTOR;
/**
* Hash-indexed array holding all set entries.
* Important!
* The actual value in this field is always an instance of Object[]
.
* Be warned that javac
emits additional casts when keys
* are directly accessed; these casts
* may result in exceptions at runtime. A workaround is to cast directly to
* Object[]
before accessing the buffer's elements (although it is highly
* recommended to use a {@link #iterator()} instead.
*
* * Direct set iteration: iterate {keys[i]} for i in [0; keys.length[ where keys[i] != null, then also * {null} is in the set if {@link #allocatedDefaultKey} = true. *
*Direct iteration warning:
* If the iteration goal is to fill another hash container, please iterate {@link #keys} in reverse to prevent performance losses.
*/
public KType[] keys;
/**
* True if key = null is in the map.
*/
public boolean allocatedDefaultKey = false;
/**
* Cached number of assigned slots in {@link #keys}.
*/
protected int assigned;
/**
* The load factor for this map (fraction of allocated slots
* before the buffers must be rehashed or reallocated).
*/
protected float loadFactor;
/**
* Resize buffers when {@link #keys} hits this value.
*/
protected int resizeAt;
/**
* The most recent slot accessed in {@link #contains}.
*
* @see #contains
* @see #lkey
*/
protected int lastSlot;
/**
* Creates a hash set with the default capacity of {@value #DEFAULT_CAPACITY},
* load factor of {@value #DEFAULT_LOAD_FACTOR}.
*/
public ObjectOpenIdentityHashSet()
{
this(ObjectOpenIdentityHashSet.DEFAULT_CAPACITY, ObjectOpenIdentityHashSet.DEFAULT_LOAD_FACTOR);
}
/**
* Creates a hash set with the given capacity,
* load factor of {@value #DEFAULT_LOAD_FACTOR}.
*/
public ObjectOpenIdentityHashSet(final int initialCapacity)
{
this(initialCapacity, ObjectOpenIdentityHashSet.DEFAULT_LOAD_FACTOR);
}
/**
* Creates a hash set with the given capacity and load factor.
*/
public ObjectOpenIdentityHashSet(final int initialCapacity, final float loadFactor)
{
assert loadFactor > 0 && loadFactor <= 1 : "Load factor must be between (0, 1].";
this.loadFactor = loadFactor;
//take into account of the load factor to garantee no reallocations before reaching initialCapacity.
int internalCapacity = (int) (initialCapacity / loadFactor) + ObjectOpenIdentityHashSet.MIN_CAPACITY;
//align on next power of two
internalCapacity = HashContainerUtils.roundCapacity(internalCapacity);
this.keys = (Internals. This method is handy, but costly if used in tight loops (anonymous
* array passing) Saves the associated value for fast access using {@link #lkey()}. Does not release internal buffers. Important!
* If the predicate actually injects the removed keys in another hash container, you may experience performance losses.
*/
@Override
public int removeAll(final ObjectPredicate super KType> predicate)
{
final int before = this.size();
if (this.allocatedDefaultKey) {
if (predicate.apply((null)))
{
this.allocatedDefaultKey = false;
}
}
final KType[] keys = this.keys;
for (int i = 0; i < keys.length;)
{
if ((keys[i] != (null)))
{
if (predicate.apply(keys[i]))
{
this.assigned--;
shiftConflictingKeys(i);
// Repeat the check for the same i.
continue;
}
}
i++;
}
return before - this.size();
}
/**
* Create a set from a variable number of arguments or an array of slot
.
*/
protected void shiftConflictingKeys(int slotCurr)
{
// Copied nearly verbatim from fastutil's impl.
final int mask = this.keys.length - 1;
int slotPrev, slotOther;
final KType[] keys = this.keys;
while (true)
{
slotCurr = ((slotPrev = slotCurr) + 1) & mask;
while ((keys[slotCurr] != (null)))
{
slotOther = (PhiMix.hash(System.identityHashCode(keys[slotCurr])) & mask);
if (slotPrev <= slotCurr)
{
// We are on the right of the original slot.
if (slotPrev >= slotOther || slotOther > slotCurr) {
break;
}
}
else
{
// We have wrapped around.
if (slotPrev >= slotOther && slotOther > slotCurr) {
break;
}
}
slotCurr = (slotCurr + 1) & mask;
}
if (!(keys[slotCurr] != (null)))
{
break;
}
// Shift key/allocated pair.
keys[slotPrev] = keys[slotCurr];
}
//means not allocated and for GC
keys[slotPrev] = (null);
}
/**
* Returns the last key saved in a call to {@link #contains} if it returned true
.
* Precondition : {@link #contains} must have been called previously !
* @see #contains
*/
public KType lkey()
{
if (this.lastSlot == -2) {
return (null);
}
assert this.lastSlot >= 0 : "Call containsKey() first.";
assert (this.keys[this.lastSlot] != (null)) : "Last call to exists did not have any associated value.";
return this.keys[this.lastSlot];
}
/**
* @return Returns the slot of the last key looked up in a call to {@link #contains} if
* it returned true
.
* or else -2 if {@link #contains} were succesfull on key = 0/null
* @see #contains
*/
public int lslot()
{
assert this.lastSlot >= 0 || this.lastSlot == -2 : "Call contains() first.";
return this.lastSlot;
}
/**
* {@inheritDoc}
*
*
* if (map.contains(key))
* value = map.lkey();
*
*
*/
@Override
public boolean contains(final KType key)
{
if (key == (null)) {
if (this.allocatedDefaultKey) {
this.lastSlot = -2;
}
else {
this.lastSlot = -1;
}
return this.allocatedDefaultKey;
}
final int mask = this.keys.length - 1;
final KType[] keys = this.keys;
//copied straight from fastutil "fast-path"
int slot;
KType curr;
//1.1 The rehashed slot is free, return false
if ((curr = keys[slot = PhiMix.hash(System.identityHashCode(key)) & mask]) == (null)) {
this.lastSlot = -1;
return false;
}
//1.2) The rehashed entry is occupied by the key, return true
if (curr == key) {
this.lastSlot = slot;
return true;
}
//2. Hash collision, search for the key along the path
slot = (slot + 1) & mask;
while ((keys[slot] != (null)))
{
if (key == keys[slot])
{
this.lastSlot = slot;
return true;
}
slot = (slot + 1) & mask;
} //end while true
//unsuccessful search
this.lastSlot = -1;
return false;
}
/**
* {@inheritDoc}
*
*
* (both are ObjectOpenCustomHashSet)
* and
* (both have equal hash strategies defined by {@link #ObjectHashingStrategy}.equals(obj.hashStrategy))
* then, both sets are compared as follows, using their {@link #ObjectHashingStrategy}.
*/
@SuppressWarnings("unchecked")
@Override
public boolean equals(final Object obj)
{
if (obj != null)
{
if (obj == this) {
return true;
}
if (!(obj instanceof ObjectOpenIdentityHashSet)) {
return false;
}
final ObjectOpenIdentityHashSetKType
.
*/
public static