UnmodifiableSet.java

  1. /*
  2.  * Copyright (C) 2018 Stein Eldar Johnsen
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  * http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  */
  16. package net.morimekta.collect;

  17. import net.morimekta.collect.collectors.UnmodifiableSetCollector;

  18. import java.util.Arrays;
  19. import java.util.Collection;
  20. import java.util.Enumeration;
  21. import java.util.Iterator;
  22. import java.util.Objects;
  23. import java.util.Set;
  24. import java.util.function.Predicate;
  25. import java.util.stream.Collector;

  26. import static java.lang.Integer.highestOneBit;
  27. import static java.lang.Math.max;
  28. import static java.util.Objects.requireNonNull;

  29. /**
  30.  * Simple immutable set. This set is order-preserving, so when iterating
  31.  * the elements, they will come in the same order as added. If same value
  32.  * was added multiple times, the first entry counts.
  33.  *
  34.  * @param <E> The set element type.
  35.  */
  36. public final class UnmodifiableSet<E> extends UnmodifiableCollection<E> implements Set<E> {
  37.     // -------- Collectors --------

  38.     /**
  39.      * Collect stream to an unmodifiable set.
  40.      *
  41.      * @param <E> The set item type.
  42.      * @return Collector instance.
  43.      */
  44.     public static <E> Collector<E, ?, UnmodifiableSet<E>> toSet() {
  45.         return new UnmodifiableSetCollector<>();
  46.     }

  47.     // -------- Constructors --------

  48.     /**
  49.      * @param array Array of items to make set of.
  50.      * @param <E>   The set item type.
  51.      * @return The unmodifiable set.
  52.      */
  53.     @SafeVarargs
  54.     @SuppressWarnings("varargs")
  55.     public static <E> UnmodifiableSet<E> asSet(E... array) {
  56.         if (array.length == 0) {
  57.             return setOf();
  58.         }
  59.         Object[] copy = Arrays.copyOf(array, array.length);
  60.         return new UnmodifiableSet<>(copy.length, copy, makeHashTable(copy, copy.length));
  61.     }

  62.     /**
  63.      * @param iterable Iterable of items to make set of.
  64.      * @param <E>      The set item type.
  65.      * @return The unmodifiable set.
  66.      */
  67.     @SuppressWarnings("unchecked")
  68.     public static <E> UnmodifiableSet<E> asSet(Iterable<? extends E> iterable) {
  69.         if (iterable instanceof Collection) {
  70.             return asSet((Collection<E>) iterable);
  71.         }
  72.         return asSet(iterable.iterator());
  73.     }

  74.     /**
  75.      * @param iterator Iterator of items to make set of.
  76.      * @param <E>      The set item type.
  77.      * @return The unmodifiable set.
  78.      */
  79.     public static <E> UnmodifiableSet<E> asSet(Iterator<? extends E> iterator) {
  80.         if (!iterator.hasNext()) {
  81.             return setOf();
  82.         }
  83.         return UnmodifiableSet.<E>newBuilder().addAll(iterator).build();
  84.     }

  85.     /**
  86.      * @param enumeration Enumeration of items to make set of.
  87.      * @param <E>         The set item type.
  88.      * @return The unmodifiable set.
  89.      */
  90.     public static <E> UnmodifiableSet<E> asSet(Enumeration<? extends E> enumeration) {
  91.         if (!enumeration.hasMoreElements()) {
  92.             return setOf();
  93.         }
  94.         return UnmodifiableSet.<E>newBuilder().addAll(enumeration).build();
  95.     }

  96.     /**
  97.      * @param collection Collection of items to make set of.
  98.      * @param <E>        The set item type.
  99.      * @return The unmodifiable set.
  100.      */
  101.     @SuppressWarnings("unchecked")
  102.     public static <E> UnmodifiableSet<E> asSet(Collection<? extends E> collection) {
  103.         if (collection.size() == 0) {
  104.             return setOf();
  105.         }
  106.         if (collection instanceof UnmodifiableSet) {
  107.             return (UnmodifiableSet<E>) collection;
  108.         }
  109.         if (collection instanceof UnmodifiableSortedSet) {
  110.             UnmodifiableSortedSet<E> set = (UnmodifiableSortedSet<E>) collection;
  111.             return new UnmodifiableSet<>(set.length, set.array, makeHashTable(set.array, set.length));
  112.         }
  113.         if (collection instanceof Set) {
  114.             Object[] array = collection.toArray();
  115.             return new UnmodifiableSet<>(array.length, array, makeHashTable(array, array.length));
  116.         }
  117.         return UnmodifiableSet.<E>newBuilder(collection.size())
  118.                               .addAll(collection)
  119.                               .build();
  120.     }

  121.     /**
  122.      * @param <E> The set item type.
  123.      * @return The unmodifiable empty set.
  124.      */
  125.     @SuppressWarnings("unchecked")
  126.     public static <E> UnmodifiableSet<E> setOf() {
  127.         return (UnmodifiableSet<E>) EMPTY;
  128.     }

  129.     /**
  130.      * @param e   The item to make a set of.
  131.      * @param <E> The set item type.
  132.      * @return The unmodifiable singleton set.
  133.      */
  134.     public static <E> UnmodifiableSet<E> setOf(E e) {
  135.         return construct(e);
  136.     }

  137.     /**
  138.      * @param e1  The first item to make a set of.
  139.      * @param e2  The second item to make a set of.
  140.      * @param <E> The set item type.
  141.      * @return The unmodifiable set.
  142.      */
  143.     public static <E> UnmodifiableSet<E> setOf(E e1, E e2) {
  144.         return construct(e1, e2);
  145.     }

  146.     /**
  147.      * @param e1  The first item to make a set of.
  148.      * @param e2  The second item to make a set of.
  149.      * @param e3  The third item to make a set of.
  150.      * @param <E> The set item type.
  151.      * @return The unmodifiable set.
  152.      */
  153.     public static <E> UnmodifiableSet<E> setOf(E e1, E e2, E e3) {
  154.         return construct(e1, e2, e3);
  155.     }

  156.     /**
  157.      * @param e1  The first item to make a set of.
  158.      * @param e2  The second item to make a set of.
  159.      * @param e3  The third item to make a set of.
  160.      * @param e4  The fourth item to make a set of.
  161.      * @param <E> The set item type.
  162.      * @return The unmodifiable set.
  163.      */
  164.     public static <E> UnmodifiableSet<E> setOf(E e1, E e2, E e3, E e4) {
  165.         return construct(e1, e2, e3, e4);
  166.     }

  167.     /**
  168.      * @param e1  The first item to make a set of.
  169.      * @param e2  The second item to make a set of.
  170.      * @param e3  The third item to make a set of.
  171.      * @param e4  The fourth item to make a set of.
  172.      * @param e5  The fifth item to make a set of.
  173.      * @param <E> The set item type.
  174.      * @return The unmodifiable set.
  175.      */
  176.     public static <E> UnmodifiableSet<E> setOf(E e1, E e2, E e3, E e4, E e5) {
  177.         return construct(e1, e2, e3, e4, e5);
  178.     }

  179.     /**
  180.      * @param e1  The first item to make a set of.
  181.      * @param e2  The second item to make a set of.
  182.      * @param e3  The third item to make a set of.
  183.      * @param e4  The fourth item to make a set of.
  184.      * @param e5  The fifth item to make a set of.
  185.      * @param e6  The sixth item to make a set of.
  186.      * @param <E> The set item type.
  187.      * @return The unmodifiable set.
  188.      */
  189.     public static <E> UnmodifiableSet<E> setOf(E e1, E e2, E e3, E e4, E e5, E e6) {
  190.         return construct(e1, e2, e3, e4, e5, e6);
  191.     }

  192.     /**
  193.      * @param e1  The first item to make a set of.
  194.      * @param e2  The second item to make a set of.
  195.      * @param e3  The third item to make a set of.
  196.      * @param e4  The fourth item to make a set of.
  197.      * @param e5  The fifth item to make a set of.
  198.      * @param e6  The sixth item to make a set of.
  199.      * @param e7  The seventh item to make a set of.
  200.      * @param <E> The set item type.
  201.      * @return The unmodifiable set.
  202.      */
  203.     public static <E> UnmodifiableSet<E> setOf(E e1, E e2, E e3, E e4, E e5, E e6, E e7) {
  204.         return construct(e1, e2, e3, e4, e5, e6, e7);
  205.     }

  206.     /**
  207.      * @param e1  The first item to make a set of.
  208.      * @param e2  The second item to make a set of.
  209.      * @param e3  The third item to make a set of.
  210.      * @param e4  The fourth item to make a set of.
  211.      * @param e5  The fifth item to make a set of.
  212.      * @param e6  The sixth item to make a set of.
  213.      * @param e7  The seventh item to make a set of.
  214.      * @param e8  The eighth item to make a set of.
  215.      * @param <E> The set item type.
  216.      * @return The unmodifiable set.
  217.      */
  218.     public static <E> UnmodifiableSet<E> setOf(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8) {
  219.         return construct(e1, e2, e3, e4, e5, e6, e7, e8);
  220.     }

  221.     /**
  222.      * @param e1  The first item to make a set of.
  223.      * @param e2  The second item to make a set of.
  224.      * @param e3  The third item to make a set of.
  225.      * @param e4  The fourth item to make a set of.
  226.      * @param e5  The fifth item to make a set of.
  227.      * @param e6  The sixth item to make a set of.
  228.      * @param e7  The seventh item to make a set of.
  229.      * @param e8  The eighth item to make a set of.
  230.      * @param e9  The ninth item to make a set of.
  231.      * @param <E> The set item type.
  232.      * @return The unmodifiable set.
  233.      */
  234.     public static <E> UnmodifiableSet<E> setOf(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9) {
  235.         return construct(e1, e2, e3, e4, e5, e6, e7, e8, e9);
  236.     }

  237.     /**
  238.      * @param e1   The first item to make a set of.
  239.      * @param e2   The second item to make a set of.
  240.      * @param e3   The third item to make a set of.
  241.      * @param e4   The fourth item to make a set of.
  242.      * @param e5   The fifth item to make a set of.
  243.      * @param e6   The sixth item to make a set of.
  244.      * @param e7   The seventh item to make a set of.
  245.      * @param e8   The eighth item to make a set of.
  246.      * @param e9   The ninth item to make a set of.
  247.      * @param e10  The tenth item to make a set of.
  248.      * @param more More items to put in set.
  249.      * @param <E>  The set item type.
  250.      * @return The unmodifiable set.
  251.      */
  252.     @SafeVarargs
  253.     @SuppressWarnings("varargs")
  254.     public static <E> UnmodifiableSet<E> setOf(E e1, E e2, E e3, E e4, E e5, E e6, E e7, E e8, E e9, E e10, E... more) {
  255.         return UnmodifiableSet
  256.                 .<E>newBuilder(10 + more.length)
  257.                 .add(e1)
  258.                 .add(e2)
  259.                 .add(e3)
  260.                 .add(e4)
  261.                 .add(e5)
  262.                 .add(e6)
  263.                 .add(e7)
  264.                 .add(e8)
  265.                 .add(e9)
  266.                 .add(e10)
  267.                 .addAll(more)
  268.                 .build();
  269.     }

  270.     /**
  271.      * @param <E> The set item type.
  272.      * @return A builder for an unmodifiable set.
  273.      */
  274.     public static <E> UnmodifiableSet.Builder<E> newBuilder() {
  275.         return new UnmodifiableSet.Builder<>(4);
  276.     }

  277.     /**
  278.      * @param initialCapacity The initial expected capacity of the map.
  279.      * @param <E>             The set item type.
  280.      * @return A builder for an unmodifiable set.
  281.      */
  282.     public static <E> UnmodifiableSet.Builder<E> newBuilder(int initialCapacity) {
  283.         return new UnmodifiableSet.Builder<>(max(1, initialCapacity));
  284.     }

  285.     // -------- UnmodifiableCollection --------

  286.     @Override
  287.     public UnmodifiableSet<E> reversed() {
  288.         if (length < 2) {
  289.             return this;
  290.         }
  291.         var out = reversedArray();
  292.         var hashTable = makeHashTable(out, length);
  293.         return new UnmodifiableSet<>(length, out, hashTable);
  294.     }

  295.     // -------- Object --------

  296.     @Override
  297.     public boolean equals(Object obj) {
  298.         if (obj == this) {
  299.             return true;
  300.         }
  301.         if (!(obj instanceof Set)) {
  302.             return false;
  303.         }
  304.         @SuppressWarnings("rawtypes")
  305.         Set other = (Set) obj;
  306.         if (other.size() != length) {
  307.             return false;
  308.         }
  309.         for (Object o : (Set<?>) obj) {
  310.             if (!contains(o)) {
  311.                 return false;
  312.             }
  313.         }
  314.         return true;
  315.     }

  316.     @Override
  317.     public String toString() {
  318.         StringBuilder builder = new StringBuilder("{");
  319.         for (int i = 0; i < length; ++i) {
  320.             if (i > 0) {
  321.                 builder.append(", ");
  322.             }
  323.             builder.append(array[i].toString());
  324.         }
  325.         return builder.append("}").toString();
  326.     }

  327.     @Override
  328.     public int hashCode() {
  329.         if (hashCode == null) {
  330.             int hash = Objects.hash(getClass(), length);
  331.             for (int i = 0; i < length; ++i) {
  332.                 hash ^= Objects.hash(array[i]);
  333.             }
  334.             hashCode = hash;
  335.         }
  336.         return hashCode;
  337.     }

  338.     // -------- Collection --------

  339.     @Override
  340.     public boolean contains(Object o) {
  341.         if (o == null) {
  342.             return false;
  343.         }
  344.         int hash = o.hashCode();
  345.         for (int hp = hash & mask; ; hp = ++hp & mask) {
  346.             Object p = hashTable[hp];
  347.             if (p == null) {
  348.                 return false;
  349.             }
  350.             if (p.equals(o)) {
  351.                 return true;
  352.             }
  353.         }
  354.     }

  355.     // -------- UnmodifiableCollection --------

  356.     /**
  357.      * Get list with only the items matching the predicate.
  358.      *
  359.      * @param predicate The predicate to keep item in response.
  360.      * @return The filtered list.
  361.      */
  362.     @Override
  363.     public UnmodifiableSet<E> filtered(Predicate<? super E> predicate) {
  364.         return (UnmodifiableSet<E>) super.filtered(predicate);
  365.     }

  366.     // -------- UnmodifiableSet --------

  367.     /**
  368.      * A builder for making unmodifiable set.
  369.      *
  370.      * @param <E> The set item type.
  371.      */
  372.     public final static class Builder<E>
  373.             implements SetBuilder<E>, UnmodifiableCollectionBuilder<E, UnmodifiableSet<E>, Builder<E>> {
  374.         private Object[] array;
  375.         private int      size;
  376.         private Object[] hashTable;
  377.         private int      mask;
  378.         private boolean  forceRehash;

  379.         Builder(int initialSize) {
  380.             array = new Object[initialSize];
  381.             size = 0;
  382.             hashTable = new Object[hashTableSizeFor(array.length, MIN_HASH_TABLE_SIZE)];
  383.             mask = hashTable.length - 1;
  384.             forceRehash = false;
  385.         }

  386.         @Override
  387.         public Builder<E> add(E e) {
  388.             requireNonNull(e, "null item");
  389.             ensureCapacity(size + 1);
  390.             hashTableAdd(e);
  391.             return this;
  392.         }

  393.         @Override
  394.         @SafeVarargs
  395.         @SuppressWarnings("varargs")
  396.         public final Builder<E> addAll(E... items) {
  397.             checkNotNull(items);
  398.             if (items.length == 0) {
  399.                 return this;
  400.             }
  401.             ensureCapacity(size + items.length);
  402.             for (E e : items) {
  403.                 hashTableAdd(e);
  404.             }
  405.             return this;
  406.         }

  407.         @Override
  408.         public Builder<E> addAll(Collection<? extends E> collection) {
  409.             checkNotNull(collection);
  410.             if (collection.isEmpty()) {
  411.                 return this;
  412.             }
  413.             ensureCapacity(size + collection.size());
  414.             for (E e : collection) {
  415.                 hashTableAdd(e);
  416.             }
  417.             return this;
  418.         }

  419.         @Override
  420.         public UnmodifiableSet<E> build() {
  421.             forceRehash = true;  // In case the builder is continuously used.
  422.             if (size == 0) {
  423.                 return setOf();
  424.             }
  425.             return new UnmodifiableSet<>(size, array, hashTable);
  426.         }

  427.         private void ensureCapacity(int capacity) {
  428.             if (array.length < capacity) {
  429.                 Object[] old = array;
  430.                 array = new Object[max(capacity, array.length * 2)];
  431.                 System.arraycopy(old, 0, array, 0, size);
  432.                 forceRehash = true;
  433.             }
  434.             if (forceRehash) {
  435.                 hashTable = makeHashTable(array, size, hashTableSizeFor(array.length, hashTable.length));
  436.                 mask = hashTable.length - 1;
  437.                 forceRehash = false;
  438.             }
  439.         }

  440.         private void hashTableAdd(Object e) {
  441.             int hash = e.hashCode();
  442.             for (int hp = hash & mask; ; hp = ++hp & mask) {
  443.                 Object p = hashTable[hp];
  444.                 if (p == null) {
  445.                     hashTable[hp] = e;
  446.                     array[size++] = e;
  447.                     break;
  448.                 } else if (p.equals(e)) {
  449.                     break;
  450.                 }
  451.             }
  452.         }
  453.     }

  454.     // -------- Private --------

  455.     UnmodifiableSet(int size, Object[] array, Object[] hashTable) {
  456.         super(0, size, array);
  457.         this.hashTable = hashTable;
  458.         this.mask = hashTable.length - 1;
  459.     }

  460.     @Override
  461.     UnmodifiableCollection<E> newInstance(Object[] array, int length) {
  462.         return new UnmodifiableSet<>(length, array, makeHashTable(array, length));
  463.     }

  464.     @SafeVarargs
  465.     @SuppressWarnings("varargs")
  466.     static <E> UnmodifiableSet<E> construct(E... array) {
  467.         return UnmodifiableSet.<E>newBuilder(array.length).addAll(array).build();
  468.     }

  469.     private static int hashTableSizeFor(final int arraySize, int currentHashTableSize) {
  470.         int tableSize = max(highestOneBit(arraySize) << 1, max(MIN_HASH_TABLE_SIZE, currentHashTableSize));
  471.         if (tableSize < (int) (MIN_OVER_CAPACITY * (double) arraySize)) {
  472.             tableSize <<= 1;
  473.         }
  474.         return tableSize;
  475.     }

  476.     static Object[] makeHashTable(Object[] array, int size) {
  477.         return makeHashTable(array, size, hashTableSizeFor(size, MIN_HASH_TABLE_SIZE));
  478.     }

  479.     private static Object[] makeHashTable(Object[] array, int size, int hashTableSize) {
  480.         int mask = hashTableSize - 1;
  481.         Object[] hashTable = new Object[hashTableSize];
  482.         for (int i = 0; i < size; ++i) {
  483.             Object o = array[i];
  484.             // if (o == null) throw new NullPointerException("Null item at index " + i + " of " + size);
  485.             int hash = o.hashCode();
  486.             int hp = hash & mask;
  487.             for (; ; ) {
  488.                 Object p = hashTable[hp];
  489.                 if (p == null) {
  490.                     hashTable[hp] = o;
  491.                     break;
  492.                 }
  493.                 hp = ++hp & mask;
  494.             }
  495.         }

  496.         return hashTable;
  497.     }

  498.     private static final double MIN_OVER_CAPACITY   = 1.38;
  499.     private static final int    MIN_HASH_TABLE_SIZE = 2;

  500.     private static final UnmodifiableSet<Object> EMPTY = new UnmodifiableSet<>(
  501.             0, EMPTY_ARRAY, new Object[MIN_HASH_TABLE_SIZE]);

  502.     private final transient Object[] hashTable;
  503.     private final transient int      mask;

  504. }