/**
* Copyright (C) 2010 Hal Hildebrand. All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program 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 Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package wblut.core;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
// TODO: Auto-generated Javadoc
/**
* The Class OpenAddressingSet.
*
* @param <T> the generic type
* @author <a href="mailto:hal.hildebrand@gmail.com">Hal Hildebrand</a>
*/
public abstract class OpenAddressingSet<T> extends AbstractSet<T> {
/** The Constant DELETED. */
private static final Object DELETED = new Object();
/** The Constant PRIME. */
private static final int PRIME = -1640531527;
/** The Constant THRESHOLD. */
private static final float THRESHOLD = 0.75f;
/** The load. */
int load;
/** The size. */
int size = 0;
/** The table. */
Object table[];
/**
* Instantiates a new open addressing set.
*/
public OpenAddressingSet() {
this(4);
}
/**
* Instantiates a new open addressing set.
*
* @param initialCapacity the initial capacity
*/
public OpenAddressingSet(final int initialCapacity) {
init(initialCapacity);
}
/* (non-Javadoc)
* @see java.util.AbstractCollection#add(java.lang.Object)
*/
@Override
public final boolean add(final Object key) {
if (key == null) {
throw new IllegalArgumentException("Null key");
}
if (table == null) {
init(1);
} else if (size >= table.length * THRESHOLD) {
rehash();
}
return insert(key);
}
/* (non-Javadoc)
* @see java.util.AbstractCollection#clear()
*/
@Override
public void clear() {
table = null;
size = 0;
}
/* (non-Javadoc)
* @see java.lang.Object#clone()
*/
@Override
public OpenAddressingSet<T> clone() {
try {
@SuppressWarnings("unchecked")
final OpenAddressingSet<T> t = (OpenAddressingSet<T>) super.clone();
if (table != null) {
t.table = new Object[table.length];
for (int i = table.length; i-- > 0;) {
t.table[i] = table[i];
}
}
return t;
} catch (final CloneNotSupportedException e) {
throw new InternalError();
}
}
/* (non-Javadoc)
* @see java.util.AbstractCollection#contains(java.lang.Object)
*/
@Override
public boolean contains(final Object key) {
if (key == null || size == 0) {
return false;
}
final int hash = PRIME * getHash(key) >>> load;
int index = hash;
do {
final Object ob = table[index];
if (ob == null) {
return false;
}
if (equals(key, ob)) {
return true;
}
index = index + (hash | 1) & table.length - 1;
} while (index != hash);
return false;
}
/* (non-Javadoc)
* @see java.util.AbstractCollection#isEmpty()
*/
@Override
public final boolean isEmpty() {
return size() == 0;
}
/* (non-Javadoc)
* @see java.util.AbstractCollection#iterator()
*/
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
int next = 0;
public boolean hasNext() {
while (next < table.length) {
if (table[next] != null && table[next] != DELETED) {
return true;
}
next++;
}
return false;
}
@SuppressWarnings("unchecked")
public T next() {
while (next < table.length) {
if (table[next] != null && table[next] != DELETED) {
return (T) table[next++];
}
next++;
}
throw new NoSuchElementException("Enumerator");
}
public void remove() {
throw new UnsupportedOperationException(
"Remove is not supported");
}
};
}
/* (non-Javadoc)
* @see java.util.AbstractCollection#remove(java.lang.Object)
*/
@Override
public final boolean remove(final Object key) {
if (key == null) {
throw new IllegalArgumentException("Null key");
}
if (!isEmpty()) {
final int hash = PRIME * getHash(key) >>> load;
int index = hash;
do {
final Object ob = table[index];
if (ob == null) {
return false;
}
if (equals(key, ob)) {
table[index] = DELETED;
size -= 1;
return true;
}
index = index + (hash | 1) & table.length - 1;
} while (index != hash);
}
return false;
}
/* (non-Javadoc)
* @see java.util.AbstractCollection#size()
*/
@Override
public final int size() {
return size;
}
/**
* Insert.
*
* @param key the key
* @return true, if successful
*/
private boolean insert(final Object key) {
final int hash = PRIME * getHash(key) >>> load;
int index = hash;
do {
final Object ob = table[index];
if (ob == null || ob == DELETED) {
table[index] = key;
size += 1;
return true;
}
if (equals(key, ob)) {
table[index] = key;
return false;
}
index = index + (hash | 1) & table.length - 1;
} while (index != hash);
rehash();
return insert(key);
}
/**
* Rehash.
*/
private void rehash() {
final Object[] oldMap = table;
final int oldCapacity = oldMap.length;
load -= 1;
table = new Object[oldCapacity * 2];
size = 0;
for (int i = oldCapacity - 1; i >= 0; i -= 1) {
final Object ob = oldMap[i];
if (ob != null && ob != DELETED) {
insert(ob);
}
}
}
/**
* Equals.
*
* @param key the key
* @param ob the ob
* @return true, if successful
*/
abstract protected boolean equals(Object key, Object ob);
/**
* Gets the hash.
*
* @param key the key
* @return the hash
*/
abstract protected int getHash(Object key);
/**
* Inits the.
*
* @param initialCapacity the initial capacity
*/
protected void init(int initialCapacity) {
if (initialCapacity < 4) {
initialCapacity = 4;
}
int cap = 4;
load = 2;
while (cap < initialCapacity) {
load += 1;
cap += cap;
}
table = new Object[cap];
load = 32 - load;
}
}