com.yahoo.collections.ArraySet Maven / Gradle / Ivy
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.collections;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
/**
* A Set implementation with low allocation cost. It should only be used for
* small number of objects, as it is implemented as scanning an ArrayList for
* equality matches. In other words: Performance will only be acceptable for
* small sets.
*
*
* The rationale for this class is the high cost of the object identifier used
* in IdentityHashMap, where the key set is often used as an identity set.
*
*
* @author Steinar Knutsen
* @author baldersheim
*
* @param
* the type contained in the Set
*/
public final class ArraySet implements Set {
private class ArrayIterator implements Iterator {
private int i = -1;
private boolean removed = false;
@Override
public boolean hasNext() {
return i + 1 < size;
}
@SuppressWarnings("unchecked")
@Override
public E next() {
if (!hasNext()) {
throw new NoSuchElementException("No more elements available");
}
removed = false;
return (E) entries[++i];
}
@Override
public void remove() {
if (removed) {
throw new IllegalStateException(
"Trying to remove same element twice.");
}
if (i == -1) {
throw new IllegalStateException(
"Trying to remove before entering iterator.");
}
delete(i--);
removed = true;
}
}
private Object[] entries;
private int size = 0;
/**
* Create a set with an initial capacity of initSize. The internal array
* will grow automatically with a linear growth rate if more elements than
* initSize are added.
*
* @param initSize
* initial size of internal element array
*/
public ArraySet(final int initSize) {
entries = new Object[initSize];
}
/**
* Expose the index in the internal array of a given object. -1 is returned
* if the object is not present in the internal array.
*
* @param e
* an object to check whether exists in this set
* @return the index of the argument e in the internal array, or -1 if the
* object is not present
*/
public int indexOf(final Object e) {
for (int i = 0; i < size; ++i) {
if (e.equals(entries[i])) {
return i;
}
}
return -1;
}
private void clean() {
int offset = 0;
for (int i = 0; i < size; ++i) {
if (entries[i] == null) {
++offset;
} else {
entries[i - offset] = entries[i];
}
}
size -= offset;
}
private void grow() {
entries = Arrays.copyOf(entries, entries.length * 2 + 1);
}
private void append(final Object arg) {
if (size == entries.length) {
grow();
}
entries[size++] = arg;
}
@Override
public boolean add(final E arg) {
final int i = indexOf(arg);
if (i >= 0) {
return false;
}
append(arg);
return true;
}
@Override
public boolean addAll(final Collection arg) {
boolean changed = false;
for (final E entry : arg) {
changed |= add(entry);
}
return changed;
}
@Override
public void clear() {
size = 0;
}
@Override
public boolean contains(final Object arg) {
return indexOf(arg) >= 0;
}
/**
* This is an extremely expensive implementation of
* {@link Set#containsAll(Collection)}. It is implemented as O(n**2).
*/
@Override
public boolean containsAll(final Collection arg) {
for (final Object entry : arg) {
if (indexOf(entry) < 0) {
return false;
}
}
return true;
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public Iterator iterator() {
return new ArrayIterator();
}
private void delete(int i) {
if (i < 0 || i >= size) {
return;
}
--size;
while (i < size) {
entries[i] = entries[i + 1];
++i;
}
entries[i] = null;
}
@Override
public boolean remove(final Object arg) {
final int i = indexOf(arg);
if (i < 0) {
return false;
}
delete(i);
return true;
}
/**
* This is an extremely expensive implementation of
* {@link Set#removeAll(Collection)}. It is implemented as O(n**2).
*/
@Override
public boolean removeAll(final Collection arg) {
boolean changed = false;
for (final Object entry : arg) {
final int i = indexOf(entry);
if (i >= 0) {
entries[i] = null;
changed = true;
}
}
if (changed) {
clean();
}
return changed;
}
/**
* This is an extremely expensive implementation of
* {@link Set#retainAll(Collection)}. It is implemented as O(n**2).
*/
@Override
public boolean retainAll(final Collection arg) {
boolean changed = false;
for (int i = 0; i < size; ++i) {
final Object entry = entries[i];
if ( !arg.contains(entry)) {
entries[i] = null;
changed = true;
}
}
if (changed) {
clean();
}
return changed;
}
@Override
public int size() {
return size;
}
@Override
public Object[] toArray() {
return Arrays.copyOf(entries, size);
}
@SuppressWarnings("unchecked")
@Override
public T[] toArray(final T[] arg) {
return Arrays.copyOf(entries, size, (Class) arg.getClass());
}
}