|
@@ -0,0 +1,618 @@
|
|
|
|
+/**
|
|
|
|
+ * Licensed to the Apache Software Foundation (ASF) under one
|
|
|
|
+ * or more contributor license agreements. See the NOTICE file
|
|
|
|
+ * distributed with this work for additional information
|
|
|
|
+ * regarding copyright ownership. The ASF licenses this file
|
|
|
|
+ * to you under the Apache License, Version 2.0 (the
|
|
|
|
+ * "License"); you may not use this file except in compliance
|
|
|
|
+ * with the License. You may obtain a copy of the License at
|
|
|
|
+ *
|
|
|
|
+ * http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
+ *
|
|
|
|
+ * Unless required by applicable law or agreed to in writing, software
|
|
|
|
+ * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
+ * See the License for the specific language governing permissions and
|
|
|
|
+ * limitations under the License.
|
|
|
|
+ */
|
|
|
|
+package org.apache.hadoop.hdfs.util;
|
|
|
|
+
|
|
|
|
+import java.io.PrintStream;
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
+import java.util.Collection;
|
|
|
|
+import java.util.ConcurrentModificationException;
|
|
|
|
+import java.util.Iterator;
|
|
|
|
+import java.util.LinkedList;
|
|
|
|
+import java.util.List;
|
|
|
|
+import java.util.NoSuchElementException;
|
|
|
|
+
|
|
|
|
+import org.apache.commons.logging.Log;
|
|
|
|
+import org.apache.commons.logging.LogFactory;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * A low memory linked hash set implementation, which uses an array for storing
|
|
|
|
+ * the elements and linked lists for collision resolution. This class does not
|
|
|
|
+ * support null element.
|
|
|
|
+ *
|
|
|
|
+ * This class is not thread safe.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+public class LightWeightHashSet<T> implements Collection<T> {
|
|
|
|
+ /**
|
|
|
|
+ * Elements of {@link LightWeightLinkedSet}.
|
|
|
|
+ */
|
|
|
|
+ static class LinkedElement<T> {
|
|
|
|
+ protected final T element;
|
|
|
|
+
|
|
|
|
+ // reference to the next entry within a bucket linked list
|
|
|
|
+ protected LinkedElement<T> next;
|
|
|
|
+
|
|
|
|
+ //hashCode of the element
|
|
|
|
+ protected final int hashCode;
|
|
|
|
+
|
|
|
|
+ public LinkedElement(T elem, int hash) {
|
|
|
|
+ this.element = elem;
|
|
|
|
+ this.next = null;
|
|
|
|
+ this.hashCode = hash;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public String toString() {
|
|
|
|
+ return element.toString();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ protected static final float DEFAULT_MAX_LOAD_FACTOR = 0.75f;
|
|
|
|
+ protected static final float DEFAUT_MIN_LOAD_FACTOR = 0.2f;
|
|
|
|
+ protected static final int MINIMUM_CAPACITY = 16;
|
|
|
|
+
|
|
|
|
+ static final int MAXIMUM_CAPACITY = 1 << 30;
|
|
|
|
+ private static final Log LOG = LogFactory.getLog(LightWeightHashSet.class);
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * An internal array of entries, which are the rows of the hash table. The
|
|
|
|
+ * size must be a power of two.
|
|
|
|
+ */
|
|
|
|
+ protected LinkedElement<T>[] entries;
|
|
|
|
+ /** Size of the entry table. */
|
|
|
|
+ private int capacity;
|
|
|
|
+ /** The size of the set (not the entry array). */
|
|
|
|
+ protected int size = 0;
|
|
|
|
+ /** Hashmask used for determining the bucket index **/
|
|
|
|
+ private int hash_mask;
|
|
|
|
+ /** Capacity at initialization time **/
|
|
|
|
+ private final int initialCapacity;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Modification version for fail-fast.
|
|
|
|
+ *
|
|
|
|
+ * @see ConcurrentModificationException
|
|
|
|
+ */
|
|
|
|
+ protected volatile int modification = 0;
|
|
|
|
+
|
|
|
|
+ private float maxLoadFactor;
|
|
|
|
+ private float minLoadFactor;
|
|
|
|
+ private int expandMultiplier = 2;
|
|
|
|
+
|
|
|
|
+ private int expandThreshold;
|
|
|
|
+ private int shrinkThreshold;
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @param initCapacity
|
|
|
|
+ * Recommended size of the internal array.
|
|
|
|
+ * @param maxLoadFactor
|
|
|
|
+ * used to determine when to expand the internal array
|
|
|
|
+ * @param minLoadFactor
|
|
|
|
+ * used to determine when to shrink the internal array
|
|
|
|
+ */
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ public LightWeightHashSet(int initCapacity, float maxLoadFactor,
|
|
|
|
+ float minLoadFactor) {
|
|
|
|
+
|
|
|
|
+ if (maxLoadFactor <= 0 || maxLoadFactor > 1.0f)
|
|
|
|
+ throw new IllegalArgumentException("Illegal maxload factor: "
|
|
|
|
+ + maxLoadFactor);
|
|
|
|
+
|
|
|
|
+ if (minLoadFactor <= 0 || minLoadFactor > maxLoadFactor)
|
|
|
|
+ throw new IllegalArgumentException("Illegal minload factor: "
|
|
|
|
+ + minLoadFactor);
|
|
|
|
+
|
|
|
|
+ this.initialCapacity = computeCapacity(initCapacity);
|
|
|
|
+ this.capacity = this.initialCapacity;
|
|
|
|
+ this.hash_mask = capacity - 1;
|
|
|
|
+
|
|
|
|
+ this.maxLoadFactor = maxLoadFactor;
|
|
|
|
+ this.expandThreshold = (int) (capacity * maxLoadFactor);
|
|
|
|
+ this.minLoadFactor = minLoadFactor;
|
|
|
|
+ this.shrinkThreshold = (int) (capacity * minLoadFactor);
|
|
|
|
+
|
|
|
|
+ entries = new LinkedElement[capacity];
|
|
|
|
+ LOG.debug("initial capacity=" + initialCapacity + ", max load factor= "
|
|
|
|
+ + maxLoadFactor + ", min load factor= " + minLoadFactor);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public LightWeightHashSet() {
|
|
|
|
+ this(MINIMUM_CAPACITY, DEFAULT_MAX_LOAD_FACTOR, DEFAUT_MIN_LOAD_FACTOR);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public LightWeightHashSet(int minCapacity) {
|
|
|
|
+ this(minCapacity, DEFAULT_MAX_LOAD_FACTOR, DEFAUT_MIN_LOAD_FACTOR);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Check if the set is empty.
|
|
|
|
+ *
|
|
|
|
+ * @return true is set empty, false otherwise
|
|
|
|
+ */
|
|
|
|
+ public boolean isEmpty() {
|
|
|
|
+ return size == 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Return the current capacity (for testing).
|
|
|
|
+ */
|
|
|
|
+ public int getCapacity() {
|
|
|
|
+ return capacity;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Return the number of stored elements.
|
|
|
|
+ */
|
|
|
|
+ public int size() {
|
|
|
|
+ return size;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get index in the internal table for a given hash.
|
|
|
|
+ */
|
|
|
|
+ protected int getIndex(int hashCode) {
|
|
|
|
+ return hashCode & hash_mask;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Check if the set contains given element
|
|
|
|
+ *
|
|
|
|
+ * @return true if element present, false otherwise.
|
|
|
|
+ */
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ public boolean contains(final Object key) {
|
|
|
|
+ // validate key
|
|
|
|
+ if (key == null) {
|
|
|
|
+ throw new IllegalArgumentException("Null element is not supported.");
|
|
|
|
+ }
|
|
|
|
+ // find element
|
|
|
|
+ final int hashCode = ((T)key).hashCode();
|
|
|
|
+ final int index = getIndex(hashCode);
|
|
|
|
+ return containsElem(index, (T) key, hashCode);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Check if the set contains given element at given index.
|
|
|
|
+ *
|
|
|
|
+ * @return true if element present, false otherwise.
|
|
|
|
+ */
|
|
|
|
+ protected boolean containsElem(int index, final T key, int hashCode) {
|
|
|
|
+ for (LinkedElement<T> e = entries[index]; e != null; e = e.next) {
|
|
|
|
+ // element found
|
|
|
|
+ if (hashCode == e.hashCode && e.element.equals(key)) {
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // element not found
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * All all elements in the collection. Expand if necessary.
|
|
|
|
+ *
|
|
|
|
+ * @param toAdd - elements to add.
|
|
|
|
+ * @return true if the set has changed, false otherwise
|
|
|
|
+ */
|
|
|
|
+ public boolean addAll(Collection<? extends T> toAdd) {
|
|
|
|
+ boolean changed = false;
|
|
|
|
+ for (T elem : toAdd) {
|
|
|
|
+ changed |= addElem(elem);
|
|
|
|
+ }
|
|
|
|
+ expandIfNecessary();
|
|
|
|
+ return changed;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Add given element to the hash table. Expand table if necessary.
|
|
|
|
+ *
|
|
|
|
+ * @return true if the element was not present in the table, false otherwise
|
|
|
|
+ */
|
|
|
|
+ public boolean add(final T element) {
|
|
|
|
+ boolean added = addElem(element);
|
|
|
|
+ expandIfNecessary();
|
|
|
|
+ return added;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Add given element to the hash table
|
|
|
|
+ *
|
|
|
|
+ * @return true if the element was not present in the table, false otherwise
|
|
|
|
+ */
|
|
|
|
+ protected boolean addElem(final T element) {
|
|
|
|
+ // validate element
|
|
|
|
+ if (element == null) {
|
|
|
|
+ throw new IllegalArgumentException("Null element is not supported.");
|
|
|
|
+ }
|
|
|
|
+ // find hashCode & index
|
|
|
|
+ final int hashCode = element.hashCode();
|
|
|
|
+ final int index = getIndex(hashCode);
|
|
|
|
+ // return false if already present
|
|
|
|
+ if (containsElem(index, element, hashCode)) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ modification++;
|
|
|
|
+ size++;
|
|
|
|
+
|
|
|
|
+ // update bucket linked list
|
|
|
|
+ LinkedElement<T> le = new LinkedElement<T>(element, hashCode);
|
|
|
|
+ le.next = entries[index];
|
|
|
|
+ entries[index] = le;
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Remove the element corresponding to the key.
|
|
|
|
+ *
|
|
|
|
+ * @return If such element exists, return true. Otherwise, return false.
|
|
|
|
+ */
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ public boolean remove(final Object key) {
|
|
|
|
+ // validate key
|
|
|
|
+ if (key == null) {
|
|
|
|
+ throw new IllegalArgumentException("Null element is not supported.");
|
|
|
|
+ }
|
|
|
|
+ LinkedElement<T> removed = removeElem((T) key);
|
|
|
|
+ shrinkIfNecessary();
|
|
|
|
+ return removed == null ? false : true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Remove the element corresponding to the key, given key.hashCode() == index.
|
|
|
|
+ *
|
|
|
|
+ * @return If such element exists, return true. Otherwise, return false.
|
|
|
|
+ */
|
|
|
|
+ protected LinkedElement<T> removeElem(final T key) {
|
|
|
|
+ LinkedElement<T> found = null;
|
|
|
|
+ final int hashCode = key.hashCode();
|
|
|
|
+ final int index = getIndex(hashCode);
|
|
|
|
+ if (entries[index] == null) {
|
|
|
|
+ return null;
|
|
|
|
+ } else if (hashCode == entries[index].hashCode &&
|
|
|
|
+ entries[index].element.equals(key)) {
|
|
|
|
+ // remove the head of the bucket linked list
|
|
|
|
+ modification++;
|
|
|
|
+ size--;
|
|
|
|
+ found = entries[index];
|
|
|
|
+ entries[index] = found.next;
|
|
|
|
+ } else {
|
|
|
|
+ // head != null and key is not equal to head
|
|
|
|
+ // search the element
|
|
|
|
+ LinkedElement<T> prev = entries[index];
|
|
|
|
+ for (found = prev.next; found != null;) {
|
|
|
|
+ if (hashCode == found.hashCode &&
|
|
|
|
+ found.element.equals(key)) {
|
|
|
|
+ // found the element, remove it
|
|
|
|
+ modification++;
|
|
|
|
+ size--;
|
|
|
|
+ prev.next = found.next;
|
|
|
|
+ found.next = null;
|
|
|
|
+ break;
|
|
|
|
+ } else {
|
|
|
|
+ prev = found;
|
|
|
|
+ found = found.next;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return found;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Remove and return n elements from the hashtable.
|
|
|
|
+ * The order in which entries are removed is unspecified, and
|
|
|
|
+ * and may not correspond to the order in which they were inserted.
|
|
|
|
+ *
|
|
|
|
+ * @return first element
|
|
|
|
+ */
|
|
|
|
+ public List<T> pollN(int n) {
|
|
|
|
+ if (n >= size) {
|
|
|
|
+ return pollAll();
|
|
|
|
+ }
|
|
|
|
+ List<T> retList = new ArrayList<T>(n);
|
|
|
|
+ if (n == 0) {
|
|
|
|
+ return retList;
|
|
|
|
+ }
|
|
|
|
+ boolean done = false;
|
|
|
|
+ int currentBucketIndex = 0;
|
|
|
|
+
|
|
|
|
+ while (!done) {
|
|
|
|
+ LinkedElement<T> current = entries[currentBucketIndex];
|
|
|
|
+ while (current != null) {
|
|
|
|
+ retList.add(current.element);
|
|
|
|
+ current = current.next;
|
|
|
|
+ entries[currentBucketIndex] = current;
|
|
|
|
+ size--;
|
|
|
|
+ modification++;
|
|
|
|
+ if (--n == 0) {
|
|
|
|
+ done = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ currentBucketIndex++;
|
|
|
|
+ }
|
|
|
|
+ shrinkIfNecessary();
|
|
|
|
+ return retList;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Remove all elements from the set and return them. Clear the entries.
|
|
|
|
+ */
|
|
|
|
+ public List<T> pollAll() {
|
|
|
|
+ List<T> retList = new ArrayList<T>(size);
|
|
|
|
+ for (int i = 0; i < entries.length; i++) {
|
|
|
|
+ LinkedElement<T> current = entries[i];
|
|
|
|
+ while (current != null) {
|
|
|
|
+ retList.add(current.element);
|
|
|
|
+ current = current.next;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ this.clear();
|
|
|
|
+ return retList;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Get array.length elements from the set, and put them into the array.
|
|
|
|
+ */
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ public T[] pollToArray(T[] array) {
|
|
|
|
+ int currentIndex = 0;
|
|
|
|
+ LinkedElement<T> current = null;
|
|
|
|
+
|
|
|
|
+ if (array.length == 0) {
|
|
|
|
+ return array;
|
|
|
|
+ }
|
|
|
|
+ if (array.length > size) {
|
|
|
|
+ array = (T[]) java.lang.reflect.Array.newInstance(array.getClass()
|
|
|
|
+ .getComponentType(), size);
|
|
|
|
+ }
|
|
|
|
+ // do fast polling if the entire set needs to be fetched
|
|
|
|
+ if (array.length == size) {
|
|
|
|
+ for (int i = 0; i < entries.length; i++) {
|
|
|
|
+ current = entries[i];
|
|
|
|
+ while (current != null) {
|
|
|
|
+ array[currentIndex++] = current.element;
|
|
|
|
+ current = current.next;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ this.clear();
|
|
|
|
+ return array;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ boolean done = false;
|
|
|
|
+ int currentBucketIndex = 0;
|
|
|
|
+
|
|
|
|
+ while (!done) {
|
|
|
|
+ current = entries[currentBucketIndex];
|
|
|
|
+ while (current != null) {
|
|
|
|
+ array[currentIndex++] = current.element;
|
|
|
|
+ current = current.next;
|
|
|
|
+ entries[currentBucketIndex] = current;
|
|
|
|
+ size--;
|
|
|
|
+ modification++;
|
|
|
|
+ if (currentIndex == array.length) {
|
|
|
|
+ done = true;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ currentBucketIndex++;
|
|
|
|
+ }
|
|
|
|
+ shrinkIfNecessary();
|
|
|
|
+ return array;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Compute capacity given initial capacity.
|
|
|
|
+ *
|
|
|
|
+ * @return final capacity, either MIN_CAPACITY, MAX_CAPACITY, or power of 2
|
|
|
|
+ * closest to the requested capacity.
|
|
|
|
+ */
|
|
|
|
+ private int computeCapacity(int initial) {
|
|
|
|
+ if (initial < MINIMUM_CAPACITY) {
|
|
|
|
+ return MINIMUM_CAPACITY;
|
|
|
|
+ }
|
|
|
|
+ if (initial > MAXIMUM_CAPACITY) {
|
|
|
|
+ return MAXIMUM_CAPACITY;
|
|
|
|
+ }
|
|
|
|
+ int capacity = 1;
|
|
|
|
+ while (capacity < initial) {
|
|
|
|
+ capacity <<= 1;
|
|
|
|
+ }
|
|
|
|
+ return capacity;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Resize the internal table to given capacity.
|
|
|
|
+ */
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ private void resize(int cap) {
|
|
|
|
+ int newCapacity = computeCapacity(cap);
|
|
|
|
+ if (newCapacity == this.capacity) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ this.capacity = newCapacity;
|
|
|
|
+ this.expandThreshold = (int) (capacity * maxLoadFactor);
|
|
|
|
+ this.shrinkThreshold = (int) (capacity * minLoadFactor);
|
|
|
|
+ this.hash_mask = capacity - 1;
|
|
|
|
+ LinkedElement<T>[] temp = entries;
|
|
|
|
+ entries = new LinkedElement[capacity];
|
|
|
|
+ for (int i = 0; i < temp.length; i++) {
|
|
|
|
+ LinkedElement<T> curr = temp[i];
|
|
|
|
+ while (curr != null) {
|
|
|
|
+ LinkedElement<T> next = curr.next;
|
|
|
|
+ int index = getIndex(curr.hashCode);
|
|
|
|
+ curr.next = entries[index];
|
|
|
|
+ entries[index] = curr;
|
|
|
|
+ curr = next;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Checks if we need to shrink, and shrinks if necessary.
|
|
|
|
+ */
|
|
|
|
+ protected void shrinkIfNecessary() {
|
|
|
|
+ if (size < this.shrinkThreshold && capacity > initialCapacity) {
|
|
|
|
+ resize(capacity / expandMultiplier);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Checks if we need to expand, and expands if necessary.
|
|
|
|
+ */
|
|
|
|
+ protected void expandIfNecessary() {
|
|
|
|
+ if (size > this.expandThreshold && capacity < MAXIMUM_CAPACITY) {
|
|
|
|
+ resize(capacity * expandMultiplier);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public Iterator<T> iterator() {
|
|
|
|
+ return new LinkedSetIterator();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public String toString() {
|
|
|
|
+ final StringBuilder b = new StringBuilder(getClass().getSimpleName());
|
|
|
|
+ b.append("(size=").append(size).append(", modification=")
|
|
|
|
+ .append(modification).append(", entries.length=")
|
|
|
|
+ .append(entries.length).append(")");
|
|
|
|
+ return b.toString();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /** Print detailed information of this object. */
|
|
|
|
+ public void printDetails(final PrintStream out) {
|
|
|
|
+ out.print(this + ", entries = [");
|
|
|
|
+ for (int i = 0; i < entries.length; i++) {
|
|
|
|
+ if (entries[i] != null) {
|
|
|
|
+ LinkedElement<T> e = entries[i];
|
|
|
|
+ out.print("\n " + i + ": " + e);
|
|
|
|
+ for (e = e.next; e != null; e = e.next) {
|
|
|
|
+ out.print(" -> " + e);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ out.println("\n]");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private class LinkedSetIterator implements Iterator<T> {
|
|
|
|
+ /** The starting modification for fail-fast. */
|
|
|
|
+ private final int startModification = modification;
|
|
|
|
+ /** The current index of the entry array. */
|
|
|
|
+ private int index = -1;
|
|
|
|
+ /** The next element to return. */
|
|
|
|
+ private LinkedElement<T> next = nextNonemptyEntry();
|
|
|
|
+
|
|
|
|
+ private LinkedElement<T> nextNonemptyEntry() {
|
|
|
|
+ for (index++; index < entries.length && entries[index] == null; index++);
|
|
|
|
+ return index < entries.length ? entries[index] : null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean hasNext() {
|
|
|
|
+ return next != null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public T next() {
|
|
|
|
+ if (modification != startModification) {
|
|
|
|
+ throw new ConcurrentModificationException("modification="
|
|
|
|
+ + modification + " != startModification = " + startModification);
|
|
|
|
+ }
|
|
|
|
+ if (next == null) {
|
|
|
|
+ throw new NoSuchElementException();
|
|
|
|
+ }
|
|
|
|
+ final T e = next.element;
|
|
|
|
+ // find the next element
|
|
|
|
+ final LinkedElement<T> n = next.next;
|
|
|
|
+ next = n != null ? n : nextNonemptyEntry();
|
|
|
|
+ return e;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public void remove() {
|
|
|
|
+ throw new UnsupportedOperationException("Remove is not supported.");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Clear the set. Resize it to the original capacity.
|
|
|
|
+ */
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ public void clear() {
|
|
|
|
+ this.capacity = this.initialCapacity;
|
|
|
|
+ this.hash_mask = capacity - 1;
|
|
|
|
+
|
|
|
|
+ this.expandThreshold = (int) (capacity * maxLoadFactor);
|
|
|
|
+ this.shrinkThreshold = (int) (capacity * minLoadFactor);
|
|
|
|
+
|
|
|
|
+ entries = new LinkedElement[capacity];
|
|
|
|
+ size = 0;
|
|
|
|
+ modification++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public Object[] toArray() {
|
|
|
|
+ Object[] result = new Object[size];
|
|
|
|
+ return toArray(result);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ @SuppressWarnings("unchecked")
|
|
|
|
+ public <U> U[] toArray(U[] a) {
|
|
|
|
+ if (a == null) {
|
|
|
|
+ throw new NullPointerException("Input array can not be null");
|
|
|
|
+ }
|
|
|
|
+ if (a.length < size) {
|
|
|
|
+ a = (U[]) java.lang.reflect.Array.newInstance(a.getClass()
|
|
|
|
+ .getComponentType(), size);
|
|
|
|
+ }
|
|
|
|
+ int currentIndex = 0;
|
|
|
|
+ for (int i = 0; i < entries.length; i++) {
|
|
|
|
+ LinkedElement<T> current = entries[i];
|
|
|
|
+ while (current != null) {
|
|
|
|
+ a[currentIndex++] = (U) current.element;
|
|
|
|
+ current = current.next;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return a;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean containsAll(Collection<?> c) {
|
|
|
|
+ Iterator<?> iter = c.iterator();
|
|
|
|
+ while (iter.hasNext()) {
|
|
|
|
+ if (!contains(iter.next())) {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean removeAll(Collection<?> c) {
|
|
|
|
+ boolean changed = false;
|
|
|
|
+ Iterator<?> iter = c.iterator();
|
|
|
|
+ while (iter.hasNext()) {
|
|
|
|
+ changed |= remove(iter.next());
|
|
|
|
+ }
|
|
|
|
+ return changed;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Override
|
|
|
|
+ public boolean retainAll(Collection<?> c) {
|
|
|
|
+ throw new UnsupportedOperationException("retainAll is not supported.");
|
|
|
|
+ }
|
|
|
|
+}
|