Follow the public account: Java big data and data warehouse, learn big data technology

EnumSet

EnumSet is a set of enumerations provided by the Java Collection framework. It is recommended that you learn about enumerations and their progression before starting to learn about them

The specification of EnumSet

Take a look at EnumSet’s instructions, but before you do, let’s take a look at its inheritance to get a sense of what’s going on

Abstract Class AbstractSet and Cloneable, Serializable, and Enum interfaces

/**
 * A specialized {@linkSet} implementation for use with enum types. All of * the elements in an enum set must come from a single enum type that Explicitly or implicitly, when the set is created. Whether explicitly or implicitly, when EnumSets are created, all elements must come from the same enumeration variable. That is what makes it so special that you have an awful lot of ideas on what to do and what to do.  The space and time performance of this * class should be good enough to allow its use as a high-quality, typesafe * alternative to traditional <tt>int</tt>-based "bit flags." Even bulk * operations (such as <tt>containsAll</tt> and <tt>retainAll</tt>) should * run very quickly if their argument is also an enum set. * EnumSet This internal implementation is extremely compact and efficient by means of bit vectors. Its high performance in time and space makes it a high-performance, type-safe alternative to identifying bits with ints * <p>The iterator returned by The < TT >iterator</ TT > method traverses The * Elements in their < I >natural order</ I > (the order in which the enum * constants are declared). * EnumSet iterator 'The returned iterator is < I > Foo foo </ I >: It will never throw {' The returned iterator is < I > Foo foo </ I >: it will never throw {'@linkConcurrentModificationException} * and it may or may not show the effects of any modifications to the set that * occur While the iteration is in progress. * This means that it does not fail quickly and will not raise an exception if it is modified during iteration. (We also mentioned this in the previous article and demonstrated it with examples.) <p>Null elements are not permitted. Attempts to insert a Null element * will throw {@link NullPointerException}.  Attempts to test for the
 * presence of a null element or to remove one will, however, function
 * properly.
 * 
 * <P>Like most collection implementations, <tt>EnumSet</tt> is not
 * synchronized.  If multiple threads access an enum set concurrently, and at
 * least one of the threads modifies the set, it should be synchronized
 * externally.  This is typically accomplished by synchronizing on some
 * object that naturally encapsulates the enum set.  If no such object exists,
 * the set should be "wrapped" using the {@linkCollections#synchronizedSet} * method. This is best done at creation time, to prevent accidental * unsynchronized access: * As with most collections implementations, EnumSet is not synchronous (thread-safe). * If multiple threads are accessing EnumSet at the same time and more than one thread is in use, it should be locked externally (before operation). * A typical implementation is to synchronize on an object. If there is no such object. You can use the Collections synchronizedSet method to obtain the object of a thread-safe wrapper object * do this operation is executive, when it was created in order to prevent the other threads to its unexpected visit * /
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable.java.io.Serializable
{
    /** * The type of the EnumSet element */
    final Class<E> elementType;

    /** * Stores all enumeration variables */
    finalEnum<? >[] universe;private staticEnum<? >[] ZERO_LENGTH_ENUM_ARRAY =newEnum<? > [0]; EnumSet(Class<E>elementType, Enum<? >[] universe) {this.elementType = elementType;
        this.universe = universe; }}Copy the code

The use of enumsets

Created enumsets

To create an EnumSet, we must first introduce java.util.EnumSet. Unlike other Set implementations, enumsets do not provide constructors, This is because the implementation of EnumSet is an abstract class, so we must create an EnumSet using the static methods provided by EnumSet

Creates an empty EnumSet with the specified element type.EnumSet<E>  noneOf(Class<E> elementType)       
// Create an EnumSet that specifies the element type and contains all the enumerated values
<E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)
// Create an EnumSet that contains the elements of the specified range in the enumeration value
<E extends Enum<E>> EnumSet<E> range(E from, E to)
// The initial set contains the complement of the specified set
<E extends Enum<E>> EnumSet<E> complementOf(EnumSet<E> s)
// Create an EnumSet that contains all the elements of the argument
<E extends Enum<E>> EnumSet<E> of(E e)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4)
<E extends Enum<E>> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5)
<E extends Enum<E>> EnumSet<E> of(E first, E... rest)
// Create an EnumSet containing all the elements in the parameter container
<E extends Enum<E>> EnumSet<E> copyOf(EnumSet<E> s)
<E extends Enum<E>> EnumSet<E> copyOf(Collection<E> c)
Copy the code
1. Use allOf (Size)

Use the allof() method to create an EnumSet containing all enumeration variables of the specified enumeration type, so just like EnumMap we must first create an enumeration type

enum Size {
    SMALL, MEDIUM, LARGE, EXTRALARGE
}
Copy the code

code

@Test
public void create(a){
    EnumSet sizes=EnumSet.allOf(Size.class);
    System.out.println("EnumSet: " + sizes);
}

Copy the code

Output

EnumSet: [SMALL, MEDIUM, LARGE, EXTRALARGE]
Copy the code

Size. Class represents that our argument is the enumerated type we created


2. Use noneOf (Class)

Create an empty set of enumerations using the noneOf() method

@Test
public void create(a){
    EnumSet sizes2=EnumSet.noneOf(Size.class);
    System.out.println("EnumSet: " + sizes2);
}
Copy the code

Output

Empty EnumSet : []
Copy the code

Above we created an empty enumerated collection

Note: We can only insert the Size enumeration variable into the collection, not other types of variables, because our Set type is the enumerated type we declared — Size


3. Use range(e1, e2)

Create an enum set containing enumerated variables between E1 and e2 (including e1 and e2) using the range() method

@Test
public void create(a){
    EnumSet sizes3=EnumSet.range(Size.MEDIUM,Size.EXTRALARGE);
    System.out.println("EnumSet: " + sizes3);
}
Copy the code

Output

EnumSet: [MEDIUM, LARGE, EXTRALARGE]
Copy the code

Here we only contains the Size MEDIUM, the Size. The elements between EXTRALARGE.

4. The use of (e1)…

Use the of() method to create an EnumSet containing the specified enumeration variables

@Test
public void create(a){
    EnumSet<Size> sizes4 = EnumSet.of(Size.SMALL, Size.LARGE);
    System.out.println("EnumSet: " + sizes4);
}
Copy the code

Output

EnumSet2: [SMALL, LARGE]
Copy the code

Create method Summary (noneOf)

Implementation details for noneOf

We’ve just shown you what the different creation methods mean without going into the details. All of them use noneOf(),

// allOf
public static <E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType) {
    EnumSet<E> result = noneOf(elementType);
    result.addAll();
    return result;
}
// range
public static <E extends Enum<E>> EnumSet<E> range(E from, E to) {
    if (from.compareTo(to) > 0)
        throw new IllegalArgumentException(from + ">" + to);
    EnumSet<E> result = noneOf(from.getDeclaringClass());
    result.addRange(from, to);
    return result;
}
// of
public static <E extends Enum<E>> EnumSet<E> of(E e1 ... ) {
    EnumSet<E> result = noneOf(e1.getDeclaringClass());
    result.add(e1);
    result.add(e2);
    return result;
}
Copy the code

All methods call noneOf, and EnumSet is an abstract class. Instead of providing a constructor to create objects, EnumSet uses static methods to create objects. What else does it return to us? So let’s see what’s going on inside noneOf

/** * Creates an empty enum set with the specified element type@param <E> The class of the elements in the set
 * @param elementType the class object of the element type for this enum set
 * @return An empty enum set of the specified type.
 * @throws NullPointerException if <tt>elementType</tt> is null
 */
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { Enum<? >[] universe = getUniverse(elementType);if (universe == null)
        throw new ClassCastException(elementType + " not an enum");

    if (universe.length <= 64)
        return new RegularEnumSet<>(elementType, universe);
    else
        return new JumboEnumSet<>(elementType, universe);
}
Copy the code

RegularEnumSet is used when the enumeration variable of enumeration type is less than or equal to 64, otherwise JumboEnumSet is used. We always use a RegularEnumSet.

We notice that getUniverse(elementType) is called in the noneOf method above. Let’s look at what this method does. The comment information says that this method returns all enumeration constants in the enumeration, and that the returned result is cached and shared by all methods that call it.

/** * Returns all of the values comprising E. * The result is uncloned, cached, and shared by all callers. */
private static <E extends Enum<E>> E[] getUniverse(Class<E> elementType) {
    return SharedSecrets.getJavaLangAccess()
                                    .getEnumConstantsShared(elementType);
}
Copy the code

SharedSecrets is a low-level Class, we can not pay too much attention to it, just know that it can let us directly access some information about the Class object, here is according to the enumeration type of Class information to get all the enumeration constants.

Now, why is 64 the number above, and not some other number

class RegularEnumSet<E extends Enum<E>> extends EnumSet<E> {
    private static final long serialVersionUID = 3411599620347842686L;
    /** * Bit vector representation of this set. The 2^k bit indicates the * presence of universe[k] in this set. */
    private long elements = 0L;
 }
class JumboEnumSet<E extends Enum<E>> extends EnumSet<E> {
    private static final long serialVersionUID = 334349849919042784L;

    /** * Bit vector representation of this set. The ith bit of the jth * element of this array represents the presence of universe[64*j +i] * in this set. */
    private long elements[];
}
Copy the code

RegularEnumSet uses a long property to store RegularEnumSet, while JumboEnumSet uses an array of longs. This is why it is compared to 64. Long is only 64 bits and identifies up to 64 enumerations, which can only be stored in arrays.

However, we notice that there is no way to use these two classes because they are not public, which means they can only be accessed under the same package.

How do other static methods return enumsets containing elements through noneOf

Back to these static methods, let’s explore the “of” method as an example

// of
public static <E extends Enum<E>> EnumSet<E> of(E e1 ... ) {
  	// noneOf returns an empty EnumSet
    EnumSet<E> result = noneOf(e1.getDeclaringClass());
   // Then it is found that certain elements are added to the collection. Other methods do the same
    result.add(e1);
    result.add(e2);
    return result;
}
Copy the code

EnumSet Adds elements

  • add()– Adds the specified enumeration variable to EnumSet
  • addAll()Adds elements from the specified collection to EnumSet
@Test
public void add(a) {
    // Creating an EnumSet using allOf()
    EnumSet<Size> sizes1 = EnumSet.allOf(Size.class);

    // Creating an EnumSet using noneOf()
    EnumSet<Size> sizes2 = EnumSet.noneOf(Size.class);

    // Using add method
    sizes2.add(Size.MEDIUM);
    System.out.println("EnumSet Using add(): " + sizes2);

    // Using addAll() method
    sizes2.addAll(sizes1);
    System.out.println("EnumSet Using addAll(): " + sizes2);
}
Copy the code

Output

EnumSet using add(): [MEDIUM]
EnumSet using addAll(): [SMALL, MEDIUM, LARGE, EXTRALARGE]
Copy the code

So let’s just use the add method as an example of how it can add with a bit vector, and the same goes for other methods, right

/**
 * Adds the specified element to this set if it is not already present.
 *
 * @param e element to be added to this set
 * @return <tt>true</tt> if the set changed as a result of the call
 *
 * @throws NullPointerException if <tt>e</tt> is null
 */
public boolean add(E e) {
    typeCheck(e);

    long oldElements = elements;
    elements |= (1L<< ((Enum<? >)e).ordinal());returnelements ! = oldElements; }Copy the code

The add operation is actually a bit operation, changing the long value to 1 of the index of the enumeration value you passed. For example, I passed the Size.MEDIUM, the corresponding ordinal is 1, then I changed the second bit of the elements to 1.

EnumSet Gets the element

To get elements from EnumSet we use the iterator() method

@Test
public void access(a) {
    // Creating an EnumSet using allOf()
    EnumSet<Size> sizes = EnumSet.allOf(Size.class);

    Iterator<Size> iterate = sizes.iterator();

    System.out.print("EnumSet: ");
    while(iterate.hasNext()) {
        System.out.print(iterate.next());
        System.out.print(","); }}Copy the code

Output

EnumSet: SMALL, MEDIUM, LARGE, EXTRALARGE,
Copy the code

EnumSet Deletes an element

  • remove()– Deletes the specified element
  • removeAll()– Delete all elements
@Test
public void remove(a) {
    // Creating EnumSet using allOf()
    EnumSet<Size> sizes = EnumSet.allOf(Size.class);
    System.out.println("EnumSet: " + sizes);

    // Using remove()
    boolean value1 = sizes.remove(Size.MEDIUM);
    System.out.println("Is MEDIUM removed? " + value1);
    System.out.println(sizes);

    // Using removeAll()
    boolean value2 = sizes.removeAll(sizes);
    System.out.println("Are all elements removed? " + value2);
    System.out.println(sizes);

}
Copy the code

Output

EnumSet: [SMALL, MEDIUM, LARGE, EXTRALARGE]
Is MEDIUM removed? true
[SMALL, LARGE, EXTRALARGE]
Are all elements removed? true
[]
Copy the code

Other methods of EnumSet

Method Description
copyOf() Creates a copy of the EnumSet
contains() Searches the EnumSet for the specified element and returns a boolean result
isEmpty() Checks if the EnumSet is empty
size() Returns the size of the EnumSet
clear() Removes all the elements from the EnumSet

Usage scenarios of EnumSet

TreeSet provides a more efficient way to store EnumSet variables than HashSet.

An EnumSet holds only one variable of enumeration type, so the JVM already knows all possible elements in advance. That’s why enumsets are implemented internally using a series of Bits

The most valuable thing about EnumSet is its internal implementation principle, which is realized by Bit. It reflects an efficient way of data processing, and this idea is used in many places, such as calculating UV or in some scenarios indicating whether or not, the more famous is bloom filter

Note that a lot of people also call it a bit vector, and since it’s a vector it can be computed.

conclusion

  1. EnumSet is a set used to store enumeration constants, and its underlying implementation is through the bit vector, so it has a good performance.
  2. RegularEnumSet is used when the number of enumeration constants is 64, and JumboEnumSet is used when enumeration constants are 64. Because it is protected (omitted), it can only be accessed under the same package. The two subclasses simply implement the interface to EnumSet differently, depending on the value of the caller’s Enum. Callers don’t need to care about these details, so they simply need to be protected and returned to the caller via static factory classes. In the future, if there is a better algorithm, you just need to extend it to a subclass, and the caller is not aware, which is more open closed principle.
  3. All creation-creation methods call noneOf to create an empty EnumSet(RegularEnumSet or JumboEnumSet) and then add data to it by adding elements.