All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.tommyettinger.ds.EnumSet Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2024-2025 See AUTHORS file.
 *
 * 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 com.github.tommyettinger.ds;

import com.github.tommyettinger.digital.BitConversion;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

import java.util.AbstractSet;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;

/**
 * A naturally-ordered Set of Enum items. Unlike {@link java.util.EnumSet}, this does not require a Class at construction time, which can be
 * useful for serialization purposes. Instead of storing a Class, this holds a "key universe" (which is almost always the
 * same as an array returned by calling {@code values()} on an Enum type), and key universes are ideally shared between
 * compatible EnumSets. No allocation is done unless this is changing its table size and/or key universe. The natural order
 * of Enum items is in ascending order of their {@link Enum#ordinal()} values.
 * 
* The key universe is an important concept here; it is simply an array of all possible Enum values the EnumSet can use as keys, in * the specific order they are declared. You almost always get a key universe by calling {@code MyEnum.values()}, but you * can also use {@link Class#getEnumConstants()} for an Enum class. You can and generally should reuse key universes in order to * avoid allocations and/or save memory; the method {@link #noneOf(Enum[])} creates an empty EnumSet with * a given key universe. If you need to use the zero-argument constructor, you can, and the key universe will be obtained from the * first key placed into the EnumSet, though it won't be shared at first. You can also set the key universe with * {@link #clearToUniverse(Enum[])}, in the process of clearing the map. *
* This class tries to be as compatible as possible with {@link java.util.EnumSet}, though this expands on that where possible. * * @author Tommy Ettinger */ public class EnumSet extends AbstractSet> implements Set>, Iterable>, EnhancedCollection> { protected int size; protected int @Nullable[] table; protected Enum @Nullable[] universe; @Nullable protected transient EnumSetIterator iterator1; @Nullable protected transient EnumSetIterator iterator2; /** * Empty constructor; using this will postpone allocating any internal arrays until {@link #add(Enum)} is first called * (potentially indirectly). */ public EnumSet () { super(); } /** * Initializes this set so that it has exactly enough capacity as needed to contain each Enum constant defined in * {@code universe}, assuming universe stores every possible constant in one Enum type. * You almost always obtain universe from calling {@code values()} on an Enum type, and you can share one * reference to one Enum array across many EnumSet instances if you don't modify the shared array. Sharing the same * universe helps save some memory if you have (very) many EnumSet instances. *
* Because the {@code boolean} parameter here is easy to forget, you may want to prefer calling {@link #noneOf(Enum[])} * instead of using this directly. * * @param universe almost always, the result of calling {@code values()} on an Enum type; used directly, not copied * @param ignoredToDistinguish an ignored boolean that differentiates this constructor, which defined a key universe, * from one that takes contents */ public EnumSet (Enum@Nullable [] universe, boolean ignoredToDistinguish) { super(); if(universe == null) return; this.universe = universe; table = new int[universe.length + 31 >>> 5]; } /** * Initializes this set so that it has exactly enough capacity as needed to contain each Enum constant defined by the * Class {@code universeClass}, assuming universeClass is non-null. This simply calls {@link #EnumSet(Enum[], boolean)} * for convenience. Note that this constructor allocates a new array of Enum constants each time it is called, where * if you use {@link #EnumSet(Enum[], boolean)} (or its equivalent, {@link #noneOf(Enum[])}), you can reuse an * unmodified array to reduce allocations. * * @param universeClass the Class of an Enum type that defines the universe of valid Enum items this can hold */ public EnumSet (@Nullable Class> universeClass) { this(universeClass == null ? null : universeClass.getEnumConstants(), true); } /** * Initializes this set so that it holds the given Enum values, with the universe of possible Enum constants this can hold * determined by the type of the first Enum in {@code contents}. *
* This is different from {@link #EnumSet(Enum[], boolean)} in that this takes constants and puts them in the set, while the other * constructor takes all possible Enum constants, usually from calling {@code values()}. You can also specify the contents of a * new EnumSet conveniently using {@link #with(Enum[])}, which allows passing items as varargs. * * @param contents an array of Enum items to place into this set */ public EnumSet (Enum @NonNull [] contents) { super(); addAll(contents); } /** * Initializes this set so that it holds the given Enum values, with the universe of possible Enum constants this can hold * determined by the type of the first Enum in {@code contents}. * * @param contents a Collection of Enum items to place into this set */ public EnumSet (@NonNull Iterator> contents) { super(); addAll(contents); } /** * Initializes this set so that it holds the given Enum values, with the universe of possible Enum constants this can hold * determined by the type of the first Enum in {@code contents}. * * @param contents a Collection of Enum items to place into this set */ public EnumSet (@NonNull Collection> contents) { super(); addAll(contents); } /** * Copy constructor; uses a direct reference to the enum values that may be cached in {@code other}, but copies other fields. * @param other another EnumSet that will have most of its data copied, but its cached {@code values()} results will be used directly */ public EnumSet (@NonNull EnumSet other) { this.size = other.size; if(other.table != null) this.table = Arrays.copyOf(other.table, other.table.length); this.universe = other.universe; } /** * Returns the number of elements in this set (its cardinality). If this * set contains more than {@code Integer.MAX_VALUE} elements, returns * {@code Integer.MAX_VALUE}. * * @return the number of elements in this set (its cardinality) */ @Override public int size () { return size; } /** * Returns {@code true} if this set contains no elements. * * @return {@code true} if this set contains no elements */ @Override public boolean isEmpty () { return size == 0; } /** * Returns {@code true} if this set contains the specified element. * More formally, returns {@code true} if and only if this set * contains an element {@code e} such that * {@code Objects.equals(item, e)}. * * @param item element whose presence in this set is to be tested * @return {@code true} if this set contains the specified element */ @Override public boolean contains (Object item) { if(table == null || universe == null || size == 0 || !(item instanceof Enum)) return false; final Enum e = (Enum)item; final int ord = e.ordinal(); if(ord >= universe.length || universe[ord] != item) return false; final int upper = ord >>> 5; if(table.length <= upper) return false; // (1 << ord) has ord implicitly used modulo 32 return (table[upper] & 1 << ord) != 0; } /** * Returns an iterator for the items in the set. The elements are * returned in the order of their {@link Enum#ordinal()} values. Remove is supported. *

* Use the {@link EnumSetIterator} constructor for nested or multithreaded iteration. */ @Override public @NonNull Iterator> iterator () { if (iterator1 == null || iterator2 == null) { iterator1 = new EnumSetIterator(this); iterator2 = new EnumSetIterator(this); } if (!iterator1.valid) { iterator1.reset(); iterator1.valid = true; iterator2.valid = false; return iterator1; } iterator2.reset(); iterator2.valid = true; iterator1.valid = false; return iterator2; } /** * Adds the specified element to this set if it is not already present * (optional operation). More formally, adds the specified element * {@code e} to this set if the set contains no element {@code e2} * such that * {@code Objects.equals(e, e2)}. * If this set already contains the element, the call leaves the set * unchanged and returns {@code false}. In combination with the * restriction on constructors, this ensures that sets never contain * duplicate elements. * *

The stipulation above does not imply that sets must accept all * elements; sets may refuse to add any particular element, including * {@code null}, and throw an exception, as described in the * specification for {@link Collection#add Collection.add}. * Individual set implementations should clearly document any * restrictions on the elements that they may contain. * * @param item element to be added to this set * @return {@code true} if this set did not already contain the specified * element */ @Override public boolean add (@NonNull Enum item) { if(universe == null) universe = item.getDeclaringClass().getEnumConstants(); if(table == null) table = new int[universe.length + 31 >>> 5]; final int ord = item.ordinal(); if(ord >= universe.length || universe[ord] != item) throw new ClassCastException("Incompatible item for this EnumSet: " + item); final int upper = ord >>> 5; if(table.length <= upper) return false; // (1 << ord) has ord implicitly used modulo 32 boolean changed = (table[upper]) != (table[upper] |= 1 << ord); if(changed) size++; return changed; } /** * Removes the specified element from this set if it is present * (optional operation). More formally, removes an element {@code e} * such that * {@code Objects.equals(item, e)}, if * this set contains such an element. Returns {@code true} if this set * contained the element (or equivalently, if this set changed as a * result of the call). (This set will not contain the element once the * call returns.) * * @param item object to be removed from this set, if present * @return {@code true} if this set contained the specified element * @throws ClassCastException if the type of the specified element * is incompatible with this set * (optional) * @throws NullPointerException if the specified element is null and this * set does not permit null elements * (optional) * @throws UnsupportedOperationException if the {@code remove} operation * is not supported by this set */ @Override public boolean remove (Object item) { if(table == null || universe == null || size == 0 || !(item instanceof Enum)) return false; final Enum e = (Enum)item; final int ord = e.ordinal(); if(ord >= universe.length || universe[ord] != e) return false; final int upper = ord >>> 5; if(table.length <= upper) return false; // (1 << ord) has ord implicitly used modulo 32 final boolean changed = table[upper] != (table[upper] &= ~(1 << ord)); if(changed) size--; return changed; } /** * Removes all items from this unless they are also in the given Collection {@code c}. * * @param c usually another EnumSet, but not required to be */ @Override public boolean retainAll (@NonNull Collection c) { if(size == 0 || table == null || universe == null || universe.length == 0) return false; if(!(c instanceof EnumSet)) return super.retainAll(c); EnumSet es = (EnumSet)c; if(es.table == null || es.universe == null || es.size == 0 || universe[0] != es.universe[0]) { clear(); return true; } int oldSize = size; size = 0; for (int i = 0; i < table.length; i++) { size += Integer.bitCount(table[i] &= es.table[i]); } return size != oldSize; } /** * Adds all the elements in the specified collection to this collection. * * @param c usually another EnumSet, but not required to be */ @Override public boolean addAll (@NonNull Collection> c) { if(!(c instanceof EnumSet)) return super.addAll(c); EnumSet es = (EnumSet)c; if(es.universe == null || es.table == null || es.universe.length == 0) return false; if(universe == null) universe = es.universe; if(table == null) table = new int[universe.length + 31 >>> 5]; if(es.universe.length != universe.length || universe[0] != es.universe[0]) return false; int oldSize = size; size = 0; for (int i = 0; i < table.length; i++) { size += Integer.bitCount(table[i] |= es.table[i]); } return size != oldSize; } /** * Returns true if this EnumSet contains all items in the given Collection, or false otherwise. * @param c usually another EnumSet, but not required to be */ @Override public boolean containsAll (@NonNull Collection c) { if(!(c instanceof EnumSet)) return super.containsAll(c); EnumSet es = (EnumSet)c; if(es.size == 0 || es.universe == null || es.table == null || es.universe.length == 0) return true; if(size < es.size || universe == null || table == null || universe.length != es.universe.length || universe[0] != es.universe[0]) return false; for (int i = 0; i < table.length; i++) { if((~table[i] & es.table[i]) != 0) return false; } return true; } /** * Removes from this EnumSet every element in the given Collection. * @param c usually another EnumSet, but not required to be * @return {@code true} if this set changed as a result of the call */ @Override public boolean removeAll (@NonNull Collection c) { if(table == null || universe == null || universe.length == 0) return false; if(!(c instanceof EnumSet)) return super.removeAll(c); EnumSet es = (EnumSet)c; if(es.table == null || es.universe == null || es.universe.length != universe.length || es.size == 0 || universe[0] != es.universe[0]) return false; int oldSize = size; size = 0; for (int i = 0; i < table.length; i++) { size += Integer.bitCount(table[i] &= ~es.table[i]); } return size != oldSize; } /** * Adds all Enum items in the given array to this set. Returns true if this set was modified at all * in the process (that is, if any items in {@code c} were not already present in this set). * * @see #add(Enum) */ public boolean addAll (Enum@NonNull [] c) { boolean modified = false; for (int i = 0; i < c.length; i++) { modified |= add(c[i]); } return modified; } /** * Removes all the elements from this set. * The set will be empty after this call returns. * This does not change the universe of possible Enum items this can hold. */ @Override public void clear () { size = 0; if(table != null) Arrays.fill(table, 0); } /** * Removes all the elements from this set and can reset the universe of possible Enum items this can hold. * The set will be empty after this call returns. * This changes the universe of possible Enum items this can hold to match {@code universe}. * If {@code universe} is null, this resets this set to the state it would have after {@link #EnumSet()} was called. * If the table this would need is the same size as or smaller than the current table (such as if {@code universe} is the same as * the universe here), this will not allocate, but will still clear any items this holds and will set the universe to the given one. * Otherwise, this allocates and uses a new table of a larger size, with nothing in it, and uses the given universe. * This always uses {@code universe} directly, without copying. *
* This can be useful to allow an EnumSet that was created with {@link #EnumSet()} to share a universe with other EnumSets. * * @param universe the universe of possible Enum items this can hold; almost always produced by {@code values()} on an Enum */ public void clearToUniverse (Enum@Nullable [] universe) { size = 0; if (universe == null) { table = null; } else if(this.universe != null && universe.length >>> 5 <= this.universe.length >>> 5) { if(table != null) Arrays.fill(table, 0); } else { table = new int[universe.length + 31 >>> 5]; } this.universe = universe; } /** * Removes all the elements from this set and can reset the universe of possible Enum items this can hold. * The set will be empty after this call returns. * This changes the universe of possible Enum items this can hold to match the Enum constants in {@code universe}. * If {@code universe} is null, this resets this set to the state it would have after {@link #EnumSet()} was called. * If the table this would need is the same size as or smaller than the current table (such as if {@code universe} is the same as * the universe here), this will not allocate, but will still clear any items this holds and will set the universe to the given one. * Otherwise, this allocates and uses a new table of a larger size, with nothing in it, and uses the given universe. * This calls {@link Class#getEnumConstants()} if universe is non-null, which allocates a new array. *
* You may want to prefer calling {@link #clearToUniverse(Enum[])} (the overload that takes an array), because it can be used to * share one universe array between many EnumSet instances. This overload, given a Class, has to call {@link Class#getEnumConstants()} * and thus allocate a new array each time this is called. * * @param universe the Class of an Enum type that stores the universe of possible Enum items this can hold */ public void clearToUniverse (@Nullable Class> universe) { size = 0; if (universe == null) { table = null; this.universe = null; } else { Enum[] cons = universe.getEnumConstants(); if(this.universe != null && cons.length >>> 5 <= this.universe.length >>> 5) { if(table != null) Arrays.fill(table, 0); } else { table = new int[cons.length + 31 >>> 5]; } this.universe = cons; } } /** * Gets the current key universe; this is a technically-mutable array, but should never be modified. * To set the universe on an existing EnumSet (with existing contents), you can use {@link #clearToUniverse(Enum[])}. * If an EnumSet has not been initialized, just adding an item will set its key universe to match the given item. * @return the current key universe */ public Enum @Nullable[] getUniverse () { return universe; } /** * Returns the first ordinal equal to or greater than the {@code minOrdinal} of an Enum contained in the set. * If no such Enum exists, or if minOrdinal is invalid (such as if it is negative or greater than the highest ordinal in the * Enum type this holds), then {@code -1} is returned. * @param minOrdinal the index to start looking at; does not need to have an Enum present there, but must be non-negative * @return the first ordinal of an Enum contained in the set on or after the specified starting point, or {@code -1} if none can be found */ public int nextOrdinal (int minOrdinal) { if(minOrdinal < 0) return -1; int[] bits = this.table; if(bits == null) return -1; int word = minOrdinal >>> 5; int bitsLength = bits.length; if (word >= bitsLength) return -1; int bitsAtWord = bits[word] & -1 << minOrdinal; // shift implicitly is masked to bottom 31 bits if (bitsAtWord != 0) { return BitConversion.countTrailingZeros(bitsAtWord) + (word << 5); // countTrailingZeros() uses an intrinsic candidate, and should be extremely fast } for (word++; word < bitsLength; word++) { bitsAtWord = bits[word]; if (bitsAtWord != 0) { return BitConversion.countTrailingZeros(bitsAtWord) + (word << 5); } } return -1; } /** * Reduces the size of the set to the specified size. If the set is already smaller than the specified * size, no action is taken. This indiscriminately removes items from the set until the * requested newSize is reached, or until the set is empty. *
* Because the iteration order is not guaranteed by an unordered set, this can remove essentially * any item(s) from the set if it is larger than newSize. Most commonly, it removes from the start * of the iteration order. * * @param newSize the target size to try to reach by removing items, if smaller than the current size */ public void truncate (int newSize) { newSize = Math.max(0, newSize); int difference = size - newSize; if(difference <= 0 || table == null) return; int upper = 0; for (; difference >= 32; difference -= 32, upper++) { table[upper] = 0; } for (int ord = 1; difference > 0; ord <<= 1) { if((table[upper] & ord) != 0) { table[upper] ^= ord; difference--; } } } /** * Returns the first Enum contained in the set with an ordinal equal to or greater than {@code minOrdinal}. * If no such Enum exists, or if minOrdinal is invalid (such as if it is negative or greater than the highest ordinal in the * Enum type this holds), then {@code null} is returned. * @param minOrdinal the index to start looking at; does not need to have an Enum present there, but must be non-negative * @return the first Enum contained in the set on or after the specified starting point, or null if none can be found */ public Enum nextEnum (int minOrdinal) { if(minOrdinal < 0) return null; int[] bits = this.table; if(bits == null) return null; int word = minOrdinal >>> 5; int bitsLength = bits.length; if (word >= bitsLength) return null; int bitsAtWord = bits[word] & -1 << minOrdinal; // shift implicitly is masked to bottom 31 bits if (bitsAtWord != 0) { return universe[BitConversion.countTrailingZeros(bitsAtWord) + (word << 5)]; // countTrailingZeros() uses an intrinsic candidate, and should be extremely fast } for (word++; word < bitsLength; word++) { bitsAtWord = bits[word]; if (bitsAtWord != 0) { return universe[BitConversion.countTrailingZeros(bitsAtWord) + (word << 5)]; } } return null; } /** * Returns the first Enum contained in the set on or after the point where the given Enum {@code from} would occur. * If no such Enum exists then {@code null} is returned. * @param from the Enum to start looking at; does not need to be present in the set * @return the first Enum contained in the set on or after the specified starting point, or null if none can be found */ public Enum nextEnum (Enum from) { if(from == null) return null; int fromIndex = from.ordinal(); int[] bits = this.table; if(bits == null) return null; int word = fromIndex >>> 5; int bitsLength = bits.length; if (word >= bitsLength) return null; int bitsAtWord = bits[word] & -1 << fromIndex; // shift implicitly is masked to bottom 31 bits if (bitsAtWord != 0) { return universe[BitConversion.countTrailingZeros(bitsAtWord) + (word << 5)]; // countTrailingZeros() uses an intrinsic candidate, and should be extremely fast } for (word++; word < bitsLength; word++) { bitsAtWord = bits[word]; if (bitsAtWord != 0) { return universe[BitConversion.countTrailingZeros(bitsAtWord) + (word << 5)]; } } return null; } @Override public String toString () { return toString(", ", true); } public static class EnumSetIterator implements Iterator> { static private final int INDEX_ILLEGAL = -1, INDEX_ZERO = -1; public boolean hasNext; final EnumSet set; int nextIndex, currentIndex; boolean valid = true; public EnumSetIterator (EnumSet set) { this.set = set; reset(); } public void reset () { currentIndex = INDEX_ILLEGAL; nextIndex = INDEX_ZERO; findNextIndex(); } void findNextIndex () { nextIndex = set.nextOrdinal(nextIndex + 1); hasNext = nextIndex != INDEX_ILLEGAL; } /** * Returns {@code true} if the iteration has more elements. * (In other words, returns {@code true} if {@link #next} would * return an element rather than throwing an exception.) * * @return {@code true} if the iteration has more elements */ @Override public boolean hasNext () { if (!valid) {throw new RuntimeException("#iterator() cannot be used nested.");} return hasNext; } @Override public void remove () { if (currentIndex < 0) { throw new IllegalStateException("next must be called before remove."); } set.remove(set.universe[currentIndex]); currentIndex = INDEX_ILLEGAL; } @Override public Enum next () { if (!hasNext) {throw new NoSuchElementException();} if (!valid) {throw new RuntimeException("#iterator() cannot be used nested.");} currentIndex = nextIndex; findNextIndex(); return set.universe[currentIndex]; } /** * Returns a new {@link ObjectList} containing the remaining items. * Does not change the position of this iterator. */ public ObjectList> toList () { ObjectList> list = new ObjectList>(set.size()); int currentIdx = currentIndex, nextIdx = nextIndex; boolean hn = hasNext; while (hasNext) { list.add(next()); } currentIndex = currentIdx; nextIndex = nextIdx; hasNext = hn; return list; } /** * Append the remaining items that this can iterate through into the given PrimitiveCollection.OfInt. * Does not change the position of this iterator. * @param coll any modifiable PrimitiveCollection.OfInt; may have items appended into it * @return the given primitive collection */ public Collection> appendInto(Collection> coll) { int currentIdx = currentIndex, nextIdx = nextIndex; boolean hn = hasNext; while (hasNext) {coll.add(next());} currentIndex = currentIdx; nextIndex = nextIdx; hasNext = hn; return coll; } } /** * Builds an EnumSet that contains only the given element. * @param item the one item to initialize the EnumSet with * @return a new EnumSet containing {@code item} */ public static EnumSet with (Enum item) { EnumSet set = new EnumSet(); set.add(item); return set; } /** * Creates a new EnumSet that holds only the given items, but can be resized. * @param item0 an Enum item * @param item1 an Enum item * @return a new EnumSet that holds the given items */ public static EnumSet with (Enum item0, Enum item1) { EnumSet set = new EnumSet(); set.add(item0); set.add(item1); return set; } /** * Creates a new EnumSet that holds only the given items, but can be resized. * @param item0 an Enum item * @param item1 an Enum item * @param item2 an Enum item * @return a new EnumSet that holds the given items */ public static EnumSet with (Enum item0, Enum item1, Enum item2) { EnumSet set = new EnumSet(); set.add(item0); set.add(item1); set.add(item2); return set; } /** * Creates a new EnumSet that holds only the given items, but can be resized. * @param item0 an Enum item * @param item1 an Enum item * @param item2 an Enum item * @param item3 an Enum item * @return a new EnumSet that holds the given items */ public static EnumSet with (Enum item0, Enum item1, Enum item2, Enum item3) { EnumSet set = new EnumSet(); set.add(item0); set.add(item1); set.add(item2); set.add(item3); return set; } /** * Creates a new EnumSet that holds only the given items, but can be resized. * @param item0 an Enum item * @param item1 an Enum item * @param item2 an Enum item * @param item3 an Enum item * @param item4 an Enum item * @return a new EnumSet that holds the given items */ public static EnumSet with (Enum item0, Enum item1, Enum item2, Enum item3, Enum item4) { EnumSet set = new EnumSet(); set.add(item0); set.add(item1); set.add(item2); set.add(item3); set.add(item4); return set; } /** * Creates a new EnumSet that holds only the given items, but can be resized. * @param item0 an Enum item * @param item1 an Enum item * @param item2 an Enum item * @param item3 an Enum item * @param item4 an Enum item * @param item5 an Enum item * @return a new EnumSet that holds the given items */ public static EnumSet with (Enum item0, Enum item1, Enum item2, Enum item3, Enum item4, Enum item5) { EnumSet set = new EnumSet(); set.add(item0); set.add(item1); set.add(item2); set.add(item3); set.add(item4); set.add(item5); return set; } /** * Creates a new EnumSet that holds only the given items, but can be resized. * @param item0 an Enum item * @param item1 an Enum item * @param item2 an Enum item * @param item3 an Enum item * @param item4 an Enum item * @param item5 an Enum item * @param item6 an Enum item * @return a new EnumSet that holds the given items */ public static EnumSet with (Enum item0, Enum item1, Enum item2, Enum item3, Enum item4, Enum item5, Enum item6) { EnumSet set = new EnumSet(); set.add(item0); set.add(item1); set.add(item2); set.add(item3); set.add(item4); set.add(item5); set.add(item6); return set; } /** * Creates a new EnumSet that holds only the given items, but can be resized. * @param item0 an Enum item * @param item1 an Enum item * @param item2 an Enum item * @param item3 an Enum item * @param item4 an Enum item * @param item5 an Enum item * @param item6 an Enum item * @return a new EnumSet that holds the given items */ public static EnumSet with (Enum item0, Enum item1, Enum item2, Enum item3, Enum item4, Enum item5, Enum item6, Enum item7) { EnumSet set = new EnumSet(); set.add(item0); set.add(item1); set.add(item2); set.add(item3); set.add(item4); set.add(item5); set.add(item6); set.add(item7); return set; } /** * Builds an EnumSet that contains the unique elements from the given {@code array} or varargs. * @param array an array or varargs of Enum constants, which should all have the same Enum type * @return a new EnumSet containing each unique item from {@code array} */ public static EnumSet with (Enum... array) { return new EnumSet(array); } /** * Alias of {@link #with(Enum)} for compatibility. * @param item the one item to initialize the EnumSet with * @return a new EnumSet containing {@code item} */ public static EnumSet of (Enum item) { return with(item); } /** * Alias of {@link #with(Enum[])} for compatibility. * @param array an array or varargs of Enum constants, which should all have the same Enum type * @return a new EnumSet containing each unique item from {@code array} */ public static EnumSet of (Enum... array) { return with(array); } /** * Creates a new EnumSet using the given result of calling {@code values()} on an Enum type (the universe), but with no items * initially stored in the set. You can reuse the universe between EnumSet instances as long as it is not modified. *
* This is the same as calling {@link #EnumSet(Enum[], boolean)}. * * @param universe almost always, the result of calling {@code values()} on an Enum type; used directly, not copied * @return a new EnumSet with the specified universe of possible items, but none present in the set */ public static EnumSet noneOf(Enum@Nullable [] universe) { return new EnumSet(universe, true); } /** * Creates a new EnumSet using the given result of calling {@code values()} on an Enum type (the universe), and with all possible * items initially stored in the set. You can reuse the universe between EnumSet instances as long as it is not modified. * * @param universe almost always, the result of calling {@code values()} on an Enum type; used directly, not copied * @return a new EnumSet with the specified universe of possible items, and all of them present in the set */ public static EnumSet allOf(Enum@Nullable [] universe) { if(universe == null) return new EnumSet(); return new EnumSet(universe); } /** * Creates a new EnumSet using the constants from the given Class (of an Enum type), but with no items initially * stored in the set. *
* This is the same as calling {@link #EnumSet(Class)}. * * @param clazz the Class of any Enum type; you can get this from a constant with {@link Enum#getDeclaringClass()} * @return a new EnumSet with the specified universe of possible items, but none present in the set */ public static EnumSet noneOf(@Nullable Class> clazz) { if(clazz == null) return new EnumSet(); return new EnumSet(clazz.getEnumConstants(), true); } /** * Creates a new EnumSet using the constants from the given Class (of an Enum type), and with all possible items initially * stored in the set. * * @param clazz the Class of any Enum type; you can get this from a constant with {@link Enum#getDeclaringClass()} * @return a new EnumSet with the specified universe of possible items, and all of them present in the set */ public static EnumSet allOf(@Nullable Class> clazz) { if(clazz == null) return new EnumSet(); return new EnumSet(clazz.getEnumConstants()); } /** * Given another EnumSet, this creates a new EnumSet with the same universe as {@code other}, but with any elements present in other * absent in the new set, and any elements absent in other present in the new set. * * @param other another EnumSet that this will copy * @return a complemented copy of {@code other} */ public static EnumSet complementOf(EnumSet other) { if(other == null) return new EnumSet(); EnumSet coll = new EnumSet(other); for (int i = 0; i < coll.table.length - 1; i++) { coll.table[i] ^= -1; } coll.table[coll.table.length - 1] ^= -1 >>> -coll.universe.length; coll.size = coll.universe.length - other.size; return coll; } /** * Creates an EnumSet holding any Enum items in the given {@code contents}, which may be any Collection of Enum, including another * EnumSet. If given an EnumSet, this will copy its Enum universe and other information even if it is empty. * @param contents a Collection of Enum values, which may be another EnumSet * @return a new EnumSet containing the unique items in contents */ public static EnumSet copyOf(Collection> contents) { if(contents == null) throw new NullPointerException("Cannot copy a null EnumSet."); return new EnumSet(contents); } /** * Creates an EnumSet holding Enum items between the ordinals of {@code start} and {@code end}. If the ordinal of end is less than * the ordinal of start, this throws an {@link IllegalArgumentException}. * If start and end are the same, this just inserts that one Enum. * * @param start the starting inclusive Enum to insert * @param end the ending inclusive Enum to insert * @return a new EnumSet containing start, end, and any Enum constants with ordinals between them * @param the shared Enum type of both start and end * @throws IllegalArgumentException if the {@link Enum#ordinal() ordinal} of end is less than the ordinal of start */ public static > EnumSet range(Enum start, Enum end) { final int mn = start.ordinal(); final int mx = end.ordinal(); if(mx < mn) throw new IllegalArgumentException("The ordinal of " + end + " (" + mx + ") must be at least equal to the ordinal of " + start + " ("+mn+")"); final int upperMin = mn >>> 5; final int upperMax = mx >>> 5; EnumSet coll = new EnumSet(); coll.add(start); if(upperMin == upperMax){ coll.table[upperMin] = (-1 >>> ~mx) ^ (-1 >>> -mn); } else { coll.table[upperMin] = -1 << mn; for (int i = upperMin + 1; i < upperMax; i++) { coll.table[i] = -1; } coll.table[upperMax] = -1 >>> ~mx; } coll.size += mx - mn; return coll; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy