package cofh.lib.util;
import com.google.common.base.Objects;
import com.google.common.primitives.Ints;
import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
@SuppressWarnings("unchecked")
public class ArrayHashList<E extends Object> extends AbstractCollection<E> implements List<E>, Cloneable, java.io.Serializable {
private static final long serialVersionUID = 3230581060536180693L;
protected static final class Entry {
protected final Object key;
protected final int hash;
protected Entry nextInBucket;
protected Entry(Object key, int keyHash) {
this.key = key;
this.hash = keyHash;
}
}
protected static int hash(Object n) {
int h = n == null ? 0 : n.hashCode();
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
private static int roundUpToPowerOf2(int number) {
return number >= Ints.MAX_POWER_OF_TWO
? Ints.MAX_POWER_OF_TWO
: (number > 2) ? Integer.highestOneBit((number - 1) << 1) : 2;
}
private transient Object[] elementData;
protected transient int size;
protected transient int mask;
protected transient Entry[] hashTable;
protected transient int modCount;
public ArrayHashList() {
elementData = new Object[10];
hashTable = new Entry[8];
mask = 7;
}
public ArrayHashList(int size) {
elementData = new Object[size];
size = roundUpToPowerOf2(size) >> 1;
hashTable = new Entry[size];
mask = size - 1;
}
public ArrayHashList(Collection<E> col) {
int size = col.size();
elementData = new Object[size];
size = roundUpToPowerOf2(size) >> 1;
hashTable = new Entry[size];
mask = size - 1;
addAll(col);
}
@Override
public int size() {
return size;
}
protected void add(E obj, int hash) {
ensureCapacityInternal(size + 1);
elementData[size++] = obj;
insert(new Entry(obj, hash));
rehashIfNecessary();
}
@Override
public boolean add(E obj) {
int hash = hash(obj);
if (seek(obj, hash) != null)
return false;
add(obj, hash);
return true;
}
@Override
public E set(int index, E obj) {
checkElementIndex(index);
int hash = hash(obj);
if (seek(obj, hash) != null) {
// return null;
throw new IllegalArgumentException("Duplicate entries not allowed");
}
++modCount;
Entry e = seek(elementData[index], hash(elementData[index]));
delete(e);
elementData[index] = obj;
insert(new Entry(obj, hash));
return (E) e.key;
}
@Override
public void add(int index, E obj) {
checkPositionIndex(index);
int hash = hash(obj);
if (seek(obj, hash) != null)
throw new IllegalArgumentException("Duplicate entries not allowed");
if (index == size) {
add(obj, hash);
return;
}
ensureCapacityInternal(++size);
System.arraycopy(elementData, index, elementData, index + 1, size - index - 1);
elementData[index] = obj;
insert(new Entry(obj, hash));
rehashIfNecessary();
}
@Override
public boolean addAll(int index, Collection<? extends E> c) {
if (c.size() == 0)
return false;
for (E e : c)
add(index++, e);
return true;
}
@Override
public E get(int index) {
checkElementIndex(index);
return index(index);
}
@Override
public int indexOf(Object obj) {
Entry e = seek(obj, hash(obj));
if (e == null)
return -1;
Object o = e.key;
Object[] data = elementData;
int i = size;
while (i --> 0)
if (data[i] == o)
break;
return i;
}
@Override
public int lastIndexOf(Object o) {
return indexOf(o);
}
@Override
public boolean contains(Object obj) {
return seek(obj, hash(obj)) != null;
}
@Override
public E remove(int index) {
checkElementIndex(index);
E oldValue = index(index);
delete(seek(oldValue, hash(oldValue)));
fastRemove(index);
return oldValue;
}
@Override
public boolean remove(Object obj) {
Entry e = seek(obj, hash(obj));
if (e == null)
return false;
Object o = e.key;
Object[] data = elementData;
for (int i = size; i --> 0; )
if (data[i] == o) {
fastRemove(i);
break;
}
delete(e);
return true;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index + 1, elementData, index, numMoved);
elementData[--size] = null; // clear to let GC do its work
}
// { following methods (until the next }) copied mostly verbatim from ArrayList
@Override
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
for (int i = hashTable.length; i --> 0; )
hashTable[i] = null;
size = 0;
}
/**
* Trims the capacity of this <tt>ArrayHashList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayHashList</tt> instance.
*/
public void trimToSize() {
++modCount;
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size);
}
}
/**
* Increases the capacity of this <tt>ArrayHashList</tt> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
if (minCapacity > 0) {
ensureCapacityInternal(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
++modCount;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* The maximum size of array to allocate.
* Some VMs reserve some header words in an array.
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
elementData = new Object[10];
hashTable = new Entry[8];
mask = 7;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
int size = s.readInt();
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
add((E) s.readObject());
}
}
}
// }
E index(int index) {
return (E) elementData[index];
}
protected Entry seek(Object obj, int hash) {
for (Entry entry = hashTable[hash & mask];
entry != null;
entry = entry.nextInBucket)
if (hash == entry.hash && Objects.equal(obj, entry.key))
return entry;
return null;
}
protected void insert(Entry entry) {
int bucket = entry.hash & mask;
entry.nextInBucket = hashTable[bucket];
hashTable[bucket] = entry;
}
protected void delete(Entry entry) {
l: synchronized (hashTable) {
int bucket = entry.hash & mask;
Entry prev = null, cur = hashTable[bucket];
if (cur == entry) {
hashTable[bucket] = cur.nextInBucket;
break l;
}
for (; true; cur = cur.nextInBucket) {
if (cur == entry) {
prev.nextInBucket = entry.nextInBucket;
break l;
}
prev = cur;
}
}
}
protected void rehashIfNecessary() {
Entry[] old = hashTable, newTable;
if (size > old.length * 2 && old.length < Ints.MAX_POWER_OF_TWO)
synchronized (hashTable) {
int newTableSize = old.length * 2, newMask = newTableSize - 1;
newTable = hashTable = new Entry[newTableSize];
mask = newMask;
for (int bucket = old.length; bucket --> 0 ; ) {
Entry entry = old[bucket];
while (entry != null) {
Entry nextEntry = entry.nextInBucket;
int keyBucket = entry.hash & newMask;
entry.nextInBucket = newTable[keyBucket];
newTable[keyBucket] = entry;
entry = nextEntry;
}
}
}
}
@Override
public ArrayHashList<E> clone() {
return new ArrayHashList<E>(this);
}
@Override
public List<E> subList(int fromIndex, int toIndex) {
// TODO Auto-generated method stub
throw new UnsupportedOperationException();
}
@Override
public Iterator<E> iterator() {
return new Itr();
}
@Override
public ListIterator<E> listIterator() {
return listIterator(0);
}
@Override
public ListIterator<E> listIterator(int index) {
return new ListItr(index);
}
protected boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
protected boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
protected String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
protected void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
protected void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
@Override
public boolean hasNext() {
return cursor != size;
}
@Override
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayHashList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
@Override
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayHashList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
@Override
public boolean hasPrevious() {
return cursor != 0;
}
@Override
public int nextIndex() {
return cursor;
}
@Override
public int previousIndex() {
return cursor - 1;
}
@Override
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayHashList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
@Override
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayHashList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayHashList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
}