package com.miguelfonseca.completely.util; import java.util.AbstractSet; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.Set; import javax.annotation.Nullable; import static com.miguelfonseca.completely.common.Precondition.checkElement; import static com.miguelfonseca.completely.common.Precondition.checkPointer; /** * Array based implementation of the {@link Set} interface. * * <p>Note that this implementation is not synchronized. */ public class ArraySet<E> extends AbstractSet<E> implements Set<E> { protected Object[] array; /** * Constructs a new {@link ArraySet}. */ public ArraySet() { array = new Object[0]; } @Override public boolean add(@Nullable E element) { int length = array.length; if (indexOf(element, array, 0, length) >= 0) { return false; } Object[] newArray = Arrays.copyOf(array, length + 1); newArray[length] = element; array = newArray; return true; } @Override public boolean addAll(Collection<? extends E> elements) { checkPointer(elements != null); return addAll(elements.toArray()); } @Override public Iterator<E> iterator() { return new Itr(); } @Override public boolean remove(@Nullable Object element) { int length = array.length; int index = indexOf(element, array, 0, length); if (index < 0) { return false; } Object[] newArray = new Object[length - 1]; System.arraycopy(array, 0, newArray, 0, index); System.arraycopy(array, index + 1, newArray, index, length - index - 1); array = newArray; return true; } @Override public boolean removeAll(Collection<?> elements) { checkPointer(elements != null); int length = array.length; if (length > 0) { int newLength = 0; Object[] newArray = new Object[length]; // Collect elements to preserve for (int i = 0; i < length; ++i) { Object element = array[i]; if (!elements.contains(element)) { newArray[newLength++] = element; } } if (newLength != length) { // Trim array = Arrays.copyOf(newArray, newLength); return true; } } return false; } @Override public int size() { return array.length; } private boolean addAll(Object[] elements) { assert elements != null; if (elements.length <= 0) { return false; } int length = array.length; int added = 0; // Consolidate elements for (int i = 0; i < elements.length; ++i) { Object element = elements[i]; if (indexOf(element, array, 0, length) < 0 && indexOf(element, elements, 0, added) < 0) { elements[added++] = element; } } if (added <= 0) { return false; } // Concatenate elements Object[] newArray = Arrays.copyOf(array, length + added); System.arraycopy(elements, 0, newArray, length, added); array = newArray; return true; } @SuppressWarnings("checkstyle:hiddenfield") private int indexOf(Object element, Object[] array, int fromIndex, int toIndex) { assert array != null; assert fromIndex >= 0; assert toIndex >= 0; if (element == null) { for (int i = fromIndex; i < toIndex; ++i) { if (array[i] == null) { return i; } } } else { for (int i = fromIndex; i < toIndex; ++i) { if (element.equals(array[i])) { return i; } } } return -1; } private class Itr implements Iterator<E> { private int cursor; Itr() { cursor = 0; } public boolean hasNext() { return cursor < array.length; } @SuppressWarnings("unchecked") public E next() { checkElement(hasNext()); return (E) array[cursor++]; } public void remove() { throw new UnsupportedOperationException(); } } }