/**
* 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 com.hellblazer.primeMover.soot.util;
import java.util.AbstractSet;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* @author <a href="mailto:hal.hildebrand@gmail.com">Hal Hildebrand</a>
*
*/
public abstract class OpenAddressingSet<T> extends AbstractSet<T> {
private static final Object DELETED = new Object();
private static final int PRIME = -1640531527;
private static final float THRESHOLD = 0.75f;
int load;
int size = 0;
Object table[];
public OpenAddressingSet() {
this(4);
}
public OpenAddressingSet(int initialCapacity) {
init(initialCapacity);
}
@Override
public final boolean add(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);
}
@Override
public void clear() {
table = null;
size = 0;
}
@Override
public Object clone() {
try {
IdentitySet<?> t = (IdentitySet<?>) 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 (CloneNotSupportedException e) {
throw new InternalError();
}
}
@Override
public boolean contains(Object key) {
if (key == null || size == 0) {
return false;
}
int hash = PRIME * getHash(key) >>> load;
int index = hash;
do {
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;
}
@Override
public final boolean isEmpty() {
return size() == 0;
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
int next = 0;
@Override
public boolean hasNext() {
while (next < table.length) {
if (table[next] != null && table[next] != DELETED) {
return true;
}
next++;
}
return false;
}
@SuppressWarnings("unchecked")
@Override
public T next() {
while (next < table.length) {
if (table[next] != null && table[next] != DELETED) {
return (T) table[next++];
}
next++;
}
throw new NoSuchElementException("Enumerator");
}
@Override
public void remove() {
throw new UnsupportedOperationException(
"Remove is not supported");
}
};
}
@Override
public final boolean remove(Object key) {
if (key == null) {
throw new IllegalArgumentException("Null key");
}
if (!isEmpty()) {
int hash = PRIME * getHash(key) >>> load;
int index = hash;
do {
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;
}
@Override
public final int size() {
return size;
}
private boolean insert(Object key) {
int hash = PRIME * getHash(key) >>> load;
int index = hash;
do {
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);
}
private void rehash() {
Object[] oldMap = table;
int oldCapacity = oldMap.length;
load -= 1;
table = new Object[oldCapacity * 2];
size = 0;
for (int i = oldCapacity - 1; i >= 0; i -= 1) {
Object ob = oldMap[i];
if (ob != null && ob != DELETED) {
insert(ob);
}
}
}
abstract protected boolean equals(Object key, Object ob);
abstract protected int getHash(Object key);
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;
}
}