Java tutorial
/* Copyright 2013-2015 Immutables Authors and Contributors Licensed 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.immutables.ordinal; import com.google.common.collect.ForwardingSet; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.primitives.Longs; import java.util.BitSet; import java.util.Collection; import java.util.List; import java.util.Set; import static com.google.common.base.Preconditions.*; /** * Immutable set that take advantage of elements being an {@link OrdinalValue}s to provide * compact storage and efficient {@link Set#contains(Object)} and * {@link Set#containsAll(Collection)} operations. * @see OrdinalValue * @see OrdinalDomain * @see BitSet BitSet for similar internal implementation * @param <E> element type */ public abstract class ImmutableOrdinalSet<E extends OrdinalValue<E>> extends ForwardingSet<E> { ImmutableOrdinalSet() { } @SuppressWarnings("rawtypes") private static final ImmutableOrdinalSet<?> EMPTY_SET = new EmptyImmutableOrdinalSet(); /** * Returns singleton empty immutable ordinal set * @param <E> element type * @return empty set */ @SuppressWarnings("unchecked") public static <E extends OrdinalValue<E>> ImmutableOrdinalSet<E> of() { // safe unchecked: will contain no elements return (ImmutableOrdinalSet<E>) EMPTY_SET; } /** * Creates immutable ordinal set from 1 or more elements. * All elements expected to have same {@link OrdinalValue#domain()} as the first element, * otherwise exception will be thrown. * @param <E> element type * @param first first element * @param rest the rest of elements * @return empty set */ @SafeVarargs public static <E extends OrdinalValue<E>> ImmutableOrdinalSet<E> of(E first, E... rest) { OrdinalDomain<E> domain = first.domain(); if (rest.length == 0) { return new SingletonImmutableOrdinalSet<>(first); } OrdinalValue<?>[] array = new OrdinalValue<?>[1 + rest.length]; array[0] = first; System.arraycopy(rest, 0, array, 1, rest.length); return new RegularImmutableOrdinalSet<>(domain, array); } /** * Creates immutable ordinal set from iterable of elements. * All elements expected to have same {@link OrdinalValue#domain()} as the first element, * otherwise exception will be thrown. * @param <E> the element type * @param elements the elements, no nulls allowed * @return the immutable ordinal set */ @SuppressWarnings("unchecked") // Safe unchecked, elements are defined to be of E or subtypes of E // which is allowed for immutable collection public static <E extends OrdinalValue<E>> ImmutableOrdinalSet<E> copyOf(Iterable<? extends E> elements) { if (elements instanceof ImmutableOrdinalSet) { return (ImmutableOrdinalSet<E>) elements; } return constructFromArray(Iterables.toArray(elements, OrdinalValue.class)); } /** * Creates immutable ordinal set from array of elements. * All elements expected to have same {@link OrdinalValue#domain()} as the first element, * otherwise exception will be thrown. * @param <E> the element type * @param elements the elements, no nulls allowed * @return the immutable ordinal set */ public static <E extends OrdinalValue<E>> ImmutableOrdinalSet<E> copyOf(E[] elements) { return constructFromArray(elements); } @SuppressWarnings("unchecked") // Safe unchecked as element is known to be of type E private static <E extends OrdinalValue<E>> ImmutableOrdinalSet<E> constructFromArray(OrdinalValue<?>[] array) { switch (array.length) { case 0: return of(); case 1: // Safe unchecked as element is known to be of type E return new SingletonImmutableOrdinalSet<>((E) array[0]); default: return new RegularImmutableOrdinalSet<>(((E) array[0]).domain(), array); } } /** * Will throw an exception and leave the collection unmodified. * @throws UnsupportedOperationException always * @deprecated Modification operation are not supported. */ @Deprecated @Override public final boolean add(E e) { throw new UnsupportedOperationException(); } /** * Will throw an exception and leave the collection unmodified. * @throws UnsupportedOperationException always * @deprecated Modification operation are not supported. */ @Deprecated @Override public final boolean remove(Object object) { throw new UnsupportedOperationException(); } /** * Will throw an exception and leave the collection unmodified. * @throws UnsupportedOperationException always * @deprecated Modification operation are not supported. */ @Deprecated @Override public final boolean addAll(Collection<? extends E> newElements) { throw new UnsupportedOperationException(); } /** * Will throw an exception and leave the collection unmodified. * @throws UnsupportedOperationException always * @deprecated Modification operation are not supported. */ @Deprecated @Override public final boolean removeAll(Collection<?> oldElements) { throw new UnsupportedOperationException(); } /** * Will throw an exception and leave the collection unmodified. * @throws UnsupportedOperationException always * @deprecated Modification operation are not supported. */ @Deprecated @Override public final boolean retainAll(Collection<?> elementsToKeep) { throw new UnsupportedOperationException(); } /** * Will throw an exception and leave the collection unmodified. * @throws UnsupportedOperationException always * @deprecated Modification operation are not supported. */ @Deprecated @Override public final void clear() { throw new UnsupportedOperationException(); } private static class EmptyImmutableOrdinalSet<E extends OrdinalValue<E>> extends ImmutableOrdinalSet<E> { @Override protected Set<E> delegate() { return ImmutableSet.of(); } @Override public boolean isEmpty() { return true; } @Override public int size() { return 0; } @Override public boolean containsAll(Collection<?> collection) { return collection.isEmpty(); } @Override public boolean containsAny(Collection<?> collection) { return false; } @Override public void incrementCounters(int[] countersByOrdinal) { } } private static class SingletonImmutableOrdinalSet<E extends OrdinalValue<E>> extends ImmutableOrdinalSet<E> { private final E element; SingletonImmutableOrdinalSet(E element) { this.element = checkNotNull(element); } @Override public boolean contains(Object object) { return element.equals(object); } @Override public boolean containsAll(Collection<?> collection) { if (collection instanceof SingletonImmutableOrdinalSet) { return element.equals(((SingletonImmutableOrdinalSet<?>) collection).element); } return super.containsAll(collection); } @Override public boolean containsAny(Collection<?> collection) { return collection.contains(element); } @Override protected Set<E> delegate() { return ImmutableSet.of(element); } @Override public int size() { return 1; } @Override public boolean isEmpty() { return false; } @Override public void incrementCounters(int[] counters) { counters[element.ordinal()]++; } } private static class RegularImmutableOrdinalSet<E extends OrdinalValue<E>> extends ImmutableOrdinalSet<E> { private static final int BITS_PER_WORD = Longs.BYTES * Byte.SIZE; private static final int POWER_OF_TWO_WORD_BITS = 6; private final OrdinalDomain<E> domain; private final long[] vector; private final int size; RegularImmutableOrdinalSet(OrdinalDomain<E> domain, OrdinalValue<?>[] elements) { int maxOrdinal = 0; int count = 0; for (OrdinalValue<?> e : elements) { checkArgument(e.domain().equals(domain), "Element has different domain %s", e); maxOrdinal = Math.max(maxOrdinal, e.ordinal()); count++; } this.domain = domain; this.size = count; this.vector = new long[(maxOrdinal >>> POWER_OF_TWO_WORD_BITS) + 1]; fillVector(elements); } private void fillVector(OrdinalValue<?>[] elements) { for (OrdinalValue<?> e : elements) { int ordinal = e.ordinal(); int wordIndex = ordinal >>> POWER_OF_TWO_WORD_BITS; int bitIndex = ordinal - (wordIndex << POWER_OF_TWO_WORD_BITS); long word = vector[wordIndex]; if (((word >>> bitIndex) & 1) != 0) { checkArgument(false, "Duplicate element %s", e); } vector[wordIndex] = word | (1L << bitIndex); } } @Override protected Set<E> delegate() { ImmutableSet.Builder<E> builder = ImmutableSet.builder(); for (int i = 0; i < vector.length; i++) { long word = vector[i]; int wordOrdinal = i * BITS_PER_WORD; for (int bitIndex = 0; bitIndex < BITS_PER_WORD; bitIndex++) { if (((word >>> bitIndex) & 1) != 0) { builder.add(domain.get(wordOrdinal + bitIndex)); } } } return builder.build(); } @Override public boolean contains(Object object) { if (object instanceof OrdinalValue<?>) { OrdinalValue<?> value = (OrdinalValue<?>) object; if (value.domain().equals(domain)) { return containsOrdinal(value.ordinal()); } } return false; } private boolean containsOrdinal(int ordinal) { int wordIndex = ordinal >>> POWER_OF_TWO_WORD_BITS; int bitIndex = ordinal - (wordIndex << POWER_OF_TWO_WORD_BITS); return (wordIndex < vector.length) && ((vector[wordIndex] >>> bitIndex) & 1) != 0; } private boolean containsAllOrdinals(RegularImmutableOrdinalSet<?> ordinalSet) { long[] otherVector = ordinalSet.vector; long[] vector = this.vector; if (vector.length < otherVector.length) { // If other set contains more words - then it contains higher ordinals that this // just don't possess, so containsAll will be false return false; } for (int i = 0; i < otherVector.length; i++) { long v = vector[i]; long ov = otherVector[i]; if ((v & ov) != ov) { return false; } } return true; } private boolean containsAnyOrdinal(RegularImmutableOrdinalSet<?> ordinalSet) { long[] otherVector = ordinalSet.vector; long[] vector = this.vector; for (int i = 0; i < otherVector.length && i < vector.length; i++) { long v = vector[i]; long ov = otherVector[i]; if ((v & ov) > 0) { return true; } } return false; } @Override public boolean containsAny(Collection<?> collection) { int size = collection.size(); if (size == 0) { return false; } if (size == 1) { return contains(Iterables.get(collection, 0)); } if (collection instanceof RegularImmutableOrdinalSet<?>) { RegularImmutableOrdinalSet<?> otherSet = (RegularImmutableOrdinalSet<?>) collection; return otherSet.domain.equals(domain) && containsAnyOrdinal(otherSet); } return super.containsAny(collection); } @Override public boolean containsAll(Collection<?> collection) { int size = collection.size(); if (size == 0) { return true; } if (size == 1) { return contains(Iterables.get(collection, 0)); } if (collection instanceof RegularImmutableOrdinalSet<?>) { RegularImmutableOrdinalSet<?> otherSet = (RegularImmutableOrdinalSet<?>) collection; return otherSet.domain.equals(domain) && containsAllOrdinals(otherSet); } return super.containsAll(collection); } @Override public boolean isEmpty() { return false; } @Override public int size() { return size; } @Override public void incrementCounters(int[] counters) { long[] vector = this.vector; for (int i = 0; i < vector.length; i++) { long v = vector[i]; for (int ordinal = i << POWER_OF_TWO_WORD_BITS; v != 0;) { int zeroes = Long.numberOfTrailingZeros(v); if (zeroes == BITS_PER_WORD) { break; } if (zeroes == BITS_PER_WORD - 1) { v = 0; } else { v >>>= zeroes + 1; } ordinal += zeroes; counters[ordinal++]++; } } } } /** * Coarse grained method to effectively collect containment information without * re-packing internal structures to temporary collections. * <p> * For any contained element, corresponding value in array by ordinal index will be incremented. * @param counters array of counters where indexes corresponds to ordinal values * @exception RuntimeException if counters array length do not correspond to ordinal indexes of * contained values */ public abstract void incrementCounters(int[] counters); public boolean containsAny(Collection<?> collection) { for (Object object : collection) { if (contains(object)) { return true; } } return false; } /** * Build instances of {@link ImmutableOrdinalSet}. * @param <E> element type * @return builder */ public static <E extends OrdinalValue<E>> Builder<E> builder() { return new Builder<>(); } /** * Build instances of {@link ImmutableOrdinalSet}. * @param <E> element type */ public static final class Builder<E extends OrdinalValue<E>> { private final List<E> builder = Lists.newArrayListWithExpectedSize(4); Builder() { } /** * Adds add elements from the iterable. * @param elements the elements * @return {@code this} builder for chained invocation */ public Builder<E> addAll(Iterable<E> elements) { Iterables.addAll(builder, checkNotNull(elements)); return this; } /** * Adds element. * @param element the element * @return {@code this} builder for chained invocation */ public Builder<E> add(E element) { builder.add(checkNotNull(element)); return this; } /** * Builds instances of {@link ImmutableOrdinalSet} using all added elements. * @return immutable ordinal set */ public ImmutableOrdinalSet<E> build() { return ImmutableOrdinalSet.copyOf(builder); } } }