package net.i2p.router.util;
import java.io.Serializable;
import java.util.AbstractSet;
import java.util.ConcurrentModificationException;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* A small, fast Set with a maximum size, backed by a fixed-size array.
* Unsynchronized, not thread-safe.
* Null elements are not permitted.
* Not appropriate for large Sets.
*
* @since 0.9.25
*/
public class ArraySet<E> extends AbstractSet<E> implements Set<E> {
public static final int MAX_CAPACITY = 32;
private final Object[] _entries;
private final boolean _throwOnFull;
private int _size;
private int _overflowIndex;
private transient int modCount;
/**
* A fixed capacity of MAX_CAPACITY.
* Adds over capacity will throw a SetFullException.
*/
public ArraySet() {
this(MAX_CAPACITY);
}
/**
* A fixed capacity of MAX_CAPACITY.
* Adds over capacity will throw a SetFullException.
* @throws SetFullException if more than MAX_CAPACITY unique elements in c.
*/
public ArraySet(Collection<? extends E> c) {
this();
addAll(c);
}
/**
* Adds over capacity will throw a SetFullException.
*
* @param capacity the maximum size
* @throws IllegalArgumentException if capacity less than 1 or more than MAX_CAPACITY.
*/
public ArraySet(int capacity) {
this(capacity, true);
}
/**
* If throwOnFull is false,
* adds over capacity will overwrite starting at slot zero.
* This breaks the AbstractCollection invariant that
* "a Collection will always contain the specified element after add() returns",
* but it prevents unexpected exceptions.
* If throwOnFull is true, adds over capacity will throw a SetFullException.
*
* @param capacity the maximum size
* @throws IllegalArgumentException if capacity less than 1 or more than MAX_CAPACITY.
*/
public ArraySet(int capacity, boolean throwOnFull) {
if (capacity <= 0 || capacity > MAX_CAPACITY)
throw new IllegalArgumentException("bad capacity");
_entries = new Object[capacity];
_throwOnFull = throwOnFull;
}
/**
* @return -1 if not found or if o is null
*/
private int indexOf(Object o) {
if (o != null) {
for (int i = 0; i < _size; i++) {
if (o.equals(_entries[i]))
return i;
}
}
return -1;
}
/**
* @throws SetFullException if throwOnFull was true in constructor
* @throws NullPointerException if o is null
*/
@Override
public boolean add(E o) {
if (o == null)
throw new NullPointerException();
int i = indexOf(o);
if (i >= 0) {
_entries[i] = o;
return false;
}
if (_size >= _entries.length) {
if (_throwOnFull)
throw new SetFullException();
i = _overflowIndex++;
if (i >= _entries.length) {
i = 0;
_overflowIndex = 0;
}
} else {
modCount++;
i = _size++;
}
_entries[i] = o;
return true;
}
@Override
public void clear() {
if (_size != 0) {
modCount++;
for (int i = 0; i < _size; i++) {
_entries[i] = null;
}
_size = 0;
}
}
@Override
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
@Override
public boolean isEmpty() {
return _size <= 0;
}
@Override
public boolean remove(Object o) {
int i = indexOf(o);
if (i < 0)
return false;
modCount++;
_size--;
for (int j = i; j < _size; j++) {
_entries[j] = _entries[j + 1];
}
_entries[_size] = null;
return true;
}
public int size() {
return _size;
}
/**
* Supports remove.
* Supports comodification checks.
*/
public Iterator<E> iterator() {
return new ASIterator();
}
public static class SetFullException extends IllegalStateException {
private static final long serialVersionUID = 9087390587254111L;
}
/**
* Modified from CachedIteratorArrayList
*/
private class ASIterator implements Iterator<E>, Serializable {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != _size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
try {
int i = cursor;
E next = (E) _entries[i];
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArraySet.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
/**
* About 3x faster than HashSet.
*/
/****
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) {
if (args.length > 0) {
System.out.println("Test with overwrite");
Set s = new ArraySet(4, false);
for (int i = 0; i < args.length; i++) {
System.out.println("Added " + args[i] + "? " + s.add(args[i]));
System.out.println("Size is now " + s.size());
}
// toString tests the iterator
System.out.println("Set now contains" + s);
for (int i = 0; i < args.length; i++) {
System.out.println("Removed " + args[i] + "? " + s.remove(args[i]));
System.out.println("Size is now " + s.size());
}
System.out.println("\nTest with throw on full");
s = new ArraySet(4);
for (int i = 0; i < args.length; i++) {
System.out.println("Added " + args[i] + "? " + s.add(args[i]));
System.out.println("Size is now " + s.size());
}
// toString tests the iterator
System.out.println("Set now contains" + s);
for (int i = 0; i < args.length; i++) {
System.out.println("Removed " + args[i] + "? " + s.remove(args[i]));
System.out.println("Size is now " + s.size());
}
}
//java.util.List c = java.util.Arrays.asList(new String[] {"foo", "bar", "baz", "splat", "barf", "baz", "moose", "bear", "cat", "dog"} );
java.util.List c = java.util.Arrays.asList(new String[] {"foo", "bar"} );
long start = System.currentTimeMillis();
Set s = new java.util.HashSet(c);
int runs = 10000000;
for (int i = 0; i < runs; i++) {
s = new java.util.HashSet(s);
}
System.out.println("HashSet took " + (System.currentTimeMillis() - start));
start = System.currentTimeMillis();
s = new ArraySet(c);
for (int i = 0; i < runs; i++) {
s = new ArraySet(s);
}
System.out.println("ArraySet took " + (System.currentTimeMillis() - start));
}
****/
}