diff options
Diffstat (limited to 'guava/src/com/google/common/collect/ImmutableSet.java')
-rw-r--r-- | guava/src/com/google/common/collect/ImmutableSet.java | 260 |
1 files changed, 128 insertions, 132 deletions
diff --git a/guava/src/com/google/common/collect/ImmutableSet.java b/guava/src/com/google/common/collect/ImmutableSet.java index b96829c..fb60ce6 100644 --- a/guava/src/com/google/common/collect/ImmutableSet.java +++ b/guava/src/com/google/common/collect/ImmutableSet.java @@ -20,14 +20,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.GwtCompatible; -import com.google.common.annotations.VisibleForTesting; import com.google.common.primitives.Ints; import java.io.Serializable; -import java.util.Arrays; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.Set; @@ -59,10 +57,6 @@ import javax.annotation.Nullable; * outside its package as it has no public or protected constructors. Thus, * instances of this type are guaranteed to be immutable. * - * <p>See the Guava User Guide article on <a href= - * "http://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained"> - * immutable collections</a>. - * * @see ImmutableList * @see ImmutableMap * @author Kevin Bourrillion @@ -102,7 +96,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2) { - return construct(2, e1, e2); + return construct(e1, e2); } /** @@ -113,7 +107,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2, E e3) { - return construct(3, e1, e2, e3); + return construct(e1, e2, e3); } /** @@ -124,7 +118,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4) { - return construct(4, e1, e2, e3, e4); + return construct(e1, e2, e3, e4); } /** @@ -135,7 +129,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any element is null */ public static <E> ImmutableSet<E> of(E e1, E e2, E e3, E e4, E e5) { - return construct(5, e1, e2, e3, e4, e5); + return construct(e1, e2, e3, e4, e5); } /** @@ -156,72 +150,59 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> elements[3] = e4; elements[4] = e5; elements[5] = e6; - System.arraycopy(others, 0, elements, paramCount, others.length); - return construct(elements.length, elements); + for (int i = paramCount; i < elements.length; i++) { + elements[i] = others[i - paramCount]; + } + return construct(elements); } - /** - * Constructs an {@code ImmutableSet} from the first {@code n} elements of the specified array. - * If {@code k} is the size of the returned {@code ImmutableSet}, then the unique elements of - * {@code elements} will be in the first {@code k} positions, and {@code elements[i] == null} for - * {@code k <= i < n}. - * - * <p>This may modify {@code elements}. Additionally, if {@code n == elements.length} and - * {@code elements} contains no duplicates, {@code elements} may be used without copying in the - * returned {@code ImmutableSet}, in which case it may no longer be modified. - * - * <p>{@code elements} may contain only values of type {@code E}. - * - * @throws NullPointerException if any of the first {@code n} elements of {@code elements} is - * null - */ - private static <E> ImmutableSet<E> construct(int n, Object... elements) { - switch (n) { - case 0: - return of(); - case 1: - @SuppressWarnings("unchecked") // safe; elements contains only E's - E elem = (E) elements[0]; - return of(elem); - default: - // continue below to handle the general case - } - int tableSize = chooseTableSize(n); + /** {@code elements} has to be internally created array. */ + private static <E> ImmutableSet<E> construct(Object... elements) { + int tableSize = chooseTableSize(elements.length); Object[] table = new Object[tableSize]; int mask = tableSize - 1; + ArrayList<Object> uniqueElementsList = null; int hashCode = 0; - int uniques = 0; - for (int i = 0; i < n; i++) { - Object element = ObjectArrays.checkElementNotNull(elements[i], i); + for (int i = 0; i < elements.length; i++) { + Object element = elements[i]; int hash = element.hashCode(); for (int j = Hashing.smear(hash); ; j++) { int index = j & mask; Object value = table[index]; if (value == null) { + if (uniqueElementsList != null) { + uniqueElementsList.add(element); + } // Came to an empty slot. Put the element here. - elements[uniques++] = element; table[index] = element; hashCode += hash; break; } else if (value.equals(element)) { + if (uniqueElementsList == null) { + // first dup + uniqueElementsList = new ArrayList<Object>(elements.length); + for (int k = 0; k < i; k++) { + Object previous = elements[k]; + uniqueElementsList.add(previous); + } + } break; } } } - Arrays.fill(elements, uniques, n, null); - if (uniques == 1) { + Object[] uniqueElements = uniqueElementsList == null + ? elements + : uniqueElementsList.toArray(); + if (uniqueElements.length == 1) { // There is only one element or elements are all duplicates @SuppressWarnings("unchecked") // we are careful to only pass in E - E element = (E) elements[0]; + E element = (E) uniqueElements[0]; return new SingletonImmutableSet<E>(element, hashCode); - } else if (tableSize != chooseTableSize(uniques)) { + } else if (tableSize > 2 * chooseTableSize(uniqueElements.length)) { // Resize the table when the array includes too many duplicates. // when this happens, we have already made a copy - return construct(uniques, elements); + return construct(uniqueElements); } else { - Object[] uniqueElements = (uniques < elements.length) - ? ObjectArrays.arraysCopyOf(elements, uniques) - : elements; return new RegularImmutableSet<E>(uniqueElements, hashCode, table, mask); } } @@ -229,30 +210,18 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> // We use power-of-2 tables, and this is the highest int that's a power of 2 static final int MAX_TABLE_SIZE = Ints.MAX_POWER_OF_TWO; - // Represents how tightly we can pack things, as a maximum. - private static final double DESIRED_LOAD_FACTOR = 0.7; - // If the set has this many elements, it will "max out" the table size - private static final int CUTOFF = - (int) Math.floor(MAX_TABLE_SIZE * DESIRED_LOAD_FACTOR); + static final int CUTOFF = 1 << 29; /** * Returns an array size suitable for the backing array of a hash table that - * uses open addressing with linear probing in its implementation. The - * returned size is the smallest power of two that can hold setSize elements - * with the desired load factor. - * - * <p>Do not call this method with setSize < 2. + * uses linear probing in its implementation. The returned size is the + * smallest power of two that can hold setSize elements while being at most + * 50% full, if possible. */ - @VisibleForTesting static int chooseTableSize(int setSize) { - // Correct the size for open addressing to match desired load factor. + static int chooseTableSize(int setSize) { if (setSize < CUTOFF) { - // Round up to the next highest power of 2. - int tableSize = Integer.highestOneBit(setSize - 1) << 1; - while (tableSize * DESIRED_LOAD_FACTOR < setSize) { - tableSize <<= 1; - } - return tableSize; + return Integer.highestOneBit(setSize) << 2; } // The table can't be completely full or we'll get infinite reprobes @@ -277,7 +246,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> case 1: return of(elements[0]); default: - return construct(elements.length, elements.clone()); + return construct(elements.clone()); } } @@ -312,19 +281,9 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if any of {@code elements} is null */ public static <E> ImmutableSet<E> copyOf(Iterator<? extends E> elements) { - // We special-case for 0 or 1 elements, but anything further is madness. - if (!elements.hasNext()) { - return of(); - } - E first = elements.next(); - if (!elements.hasNext()) { - return of(first); - } else { - return new ImmutableSet.Builder<E>() - .add(first) - .addAll(elements) - .build(); - } + // TODO(benyu): here we could avoid toArray() for 0 or 1-element list, + // worth it? + return copyFromCollection(Lists.newArrayList(elements)); } /** @@ -366,12 +325,6 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> if (!set.isPartialView()) { return set; } - } else if (elements instanceof EnumSet) { - EnumSet<?> enumSet = EnumSet.copyOf((EnumSet<?>) elements); - @SuppressWarnings("unchecked") - // immutable collections are safe for covariant casts - ImmutableSet<E> result = (ImmutableSet<E>) ImmutableEnumSet.asImmutable(enumSet); - return result; } return copyFromCollection(elements); } @@ -389,7 +342,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> default: // safe to use the array without copying it // as specified by Collection.toArray(). - return construct(elements.length, elements); + return construct(elements); } } @@ -438,16 +391,30 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> return false; } + /* + * The cast is safe because the only way to create an instance is via the + * create() method above, which only permits elements of type E. + */ + @SuppressWarnings("unchecked") @Override public UnmodifiableIterator<E> iterator() { - return asList().iterator(); + return (UnmodifiableIterator<E>) Iterators.forArray(elements); } @Override public Object[] toArray() { - return asList().toArray(); + Object[] array = new Object[size()]; + System.arraycopy(elements, 0, array, 0, size()); + return array; } @Override public <T> T[] toArray(T[] array) { - return asList().toArray(array); + int size = size(); + if (array.length < size) { + array = ObjectArrays.newArray(array, size); + } else if (array.length > size) { + array[size] = null; + } + System.arraycopy(elements, 0, array, 0, size); + return array; } @Override public boolean containsAll(Collection<?> targets) { @@ -473,7 +440,65 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> } @Override ImmutableList<E> createAsList() { - return new RegularImmutableAsList<E>(this, elements); + return new ImmutableAsList<E>(elements, this); + } + } + + /** such as ImmutableMap.keySet() */ + abstract static class TransformedImmutableSet<D, E> extends ImmutableSet<E> { + final D[] source; + final int hashCode; + + TransformedImmutableSet(D[] source, int hashCode) { + this.source = source; + this.hashCode = hashCode; + } + + abstract E transform(D element); + + @Override + public int size() { + return source.length; + } + + @Override public boolean isEmpty() { + return false; + } + + @Override public UnmodifiableIterator<E> iterator() { + return new AbstractIndexedListIterator<E>(source.length) { + @Override protected E get(int index) { + return transform(source[index]); + } + }; + } + + @Override public Object[] toArray() { + return toArray(new Object[size()]); + } + + @Override public <T> T[] toArray(T[] array) { + int size = size(); + if (array.length < size) { + array = ObjectArrays.newArray(array, size); + } else if (array.length > size) { + array[size] = null; + } + + // Writes will produce ArrayStoreException when the toArray() doc requires + Object[] objectArray = array; + for (int i = 0; i < source.length; i++) { + objectArray[i] = transform(source[i]); + } + return array; + } + + @Override public final int hashCode() { + return hashCode; + } + + @Override boolean isHashCodeFast() { + return true; } } @@ -524,34 +549,14 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @since 2.0 (imported from Google Collections Library) */ public static class Builder<E> extends ImmutableCollection.Builder<E> { - Object[] contents; - int size; + // accessed directly by ImmutableSortedSet + final ArrayList<E> contents = Lists.newArrayList(); /** * Creates a new builder. The returned builder is equivalent to the builder * generated by {@link ImmutableSet#builder}. */ - public Builder() { - this(DEFAULT_INITIAL_CAPACITY); - } - - Builder(int capacity) { - checkArgument(capacity >= 0, "capacity must be >= 0 but was %s", capacity); - this.contents = new Object[capacity]; - this.size = 0; - } - - /** - * Expand the absolute capacity of the builder so it can accept at least - * the specified number of elements without being resized. - */ - Builder<E> ensureCapacity(int minCapacity) { - if (contents.length < minCapacity) { - contents = ObjectArrays.arraysCopyOf( - contents, expandedCapacity(contents.length, minCapacity)); - } - return this; - } + public Builder() {} /** * Adds {@code element} to the {@code ImmutableSet}. If the {@code @@ -563,8 +568,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * @throws NullPointerException if {@code element} is null */ @Override public Builder<E> add(E element) { - ensureCapacity(size + 1); - contents[size++] = checkNotNull(element); + contents.add(checkNotNull(element)); return this; } @@ -578,12 +582,8 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * null element */ @Override public Builder<E> add(E... elements) { - for (int i = 0; i < elements.length; i++) { - ObjectArrays.checkElementNotNull(elements[i], i); - } - ensureCapacity(size + elements.length); - System.arraycopy(elements, 0, contents, size, elements.length); - size += elements.length; + contents.ensureCapacity(contents.size() + elements.length); + super.add(elements); return this; } @@ -599,7 +599,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> @Override public Builder<E> addAll(Iterable<? extends E> elements) { if (elements instanceof Collection) { Collection<?> collection = (Collection<?>) elements; - ensureCapacity(size + collection.size()); + contents.ensureCapacity(contents.size() + collection.size()); } super.addAll(elements); return this; @@ -624,11 +624,7 @@ public abstract class ImmutableSet<E> extends ImmutableCollection<E> * the {@code Builder}. */ @Override public ImmutableSet<E> build() { - ImmutableSet<E> result = construct(size, contents); - // construct has the side effect of deduping contents, so we update size - // accordingly. - size = result.size(); - return result; + return copyOf(contents); } } } |