existEffectiveJavaWhy do you propose to replace the bit field with EnumSet in clause 36 and the ordinal index with EnumMap in clause 37?
EnumSet
existEffectiveJavaThe proposal to replace the bit field with an EnumSet in section 36 of the
36. Replacing Bit Fields with EnumSet
If the elements of an enumeration type are mainly used in Sets, the int enumeration pattern is traditionally used, with each constant being assigned a value by a different 2-squared number:
// Bit field enumeration constants - OBSOLETE!
public class Text {
public static final int STYLE_BOLD = 1 << 0; // 1
public static final int STYLE_ITALIC = 1 << 1; // 2
public static final int STYLE_UNDERLINE = 1 << 2; // 4
public static final int STYLE_STRIKETHROUGH = 1 << 3; // 8
// Parameter is bitwise OR of zero or more STYLE_ constants
public void applyStyles(int styles) { ... }
}
This representation, called a bit field, allows you to combine several constants into a Set using the or operation of bitwise arithmetic:
(STYLE_BOLD | STYLE_ITALIC);
Bit fields have all the disadvantages of int enumeration constants, for example:
- Difficult to understand when printed as a number
- There is no easy way to iterate through all the elements of a bit field
- Once the storage type of int or long as a bit field has been determined, it cannot be exceeded (32-bit or 64-bit)
The EnumSet class is a better alternative that avoids the above drawbacks. And since it is similar to bit operations in its underlying implementation, it is comparable in performance to bit fields.
Modify the previous example to use the EnumSet method for greater simplicity and clarity:
// EnumSet - a modern replacement for bit fields
public class Text {
public enum Style { BOLD, ITALIC, UNDERLINE, STRIKETHROUGH }
// Any Set could be passed in, but EnumSet is clearly best
public void applyStyles(Set<Style> styles) { ... }
}
The following is the user code that passes an EnumSet instance to the applyStyles method.The EnumSet class provides a rich set of static factories that make it easy to create Sets:
((, ));
EnumSet is a specialized Set collection used with enumeration types. All elements in an EnumSet must be of enumeration type.
With other Set interface implementation class HashSet/TreeSet (internal are implemented using the corresponding HashMap/TreeMap) is different, EnumSet in the internal implementation of bit vectors, it is an extremely efficient bitwise operation, because the direct storage and operation are bit, so EnumSet space and time performance is very considerable, comparable to the traditional int-based "bit flag" operation. It is an extremely efficient bitwise operation, and because it directly stores and manipulates bits, EnumSet is very efficient in terms of space and time, comparable to traditional int-based "bit-flag" operations. Importantly, we can manipulate bitwise operations in the same way as we can manipulate set collections, which makes the code easier to understand and has the advantage of being type-safe.
take note of: EnumSet does not allow null elements. Attempting to insert a null element will throw a NullPointerException, but attempting to test to determine if a null element exists or to remove a null element will not throw an exception. Like most collection implementations, EnumSet is not thread-safe, so care should be taken with data synchronization in a multi-threaded environment.
So how is EnumSet used and what does the underlying implementation look like?
basic usage
You can't use the new keyword to create an EnumSet because it's an abstract class. Instead, you should use the static factory methods it provides, and there are more static factory methods for EnumSet, as follows:
// Create 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 enumeration values
<E extends Enum<E>> EnumSet<E> allOf(Class<E> elementType)
// Create an EnumSet that includes the specified range of elements in the enumeration value
<E extends Enum<E> > EnumSet<E> range(E from, E to)
// The initial set consists of the complement of the specified set
<E extends Enum<E> > EnumSet<E> complementOf(EnumSet<E> s)
// Create an EnumSet that includes all the elements in 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)
The code demo is as follows:
enum Color {
green , red , blue , black , yellow
}
public class EnumSetDemo {
public static void main(String[] args){
//Empty set
EnumSet<Color> enumSet= ().
("Before adding: "+());// Before adding:[]
();
();
();
().
().
("After adding: "+());//After adding:[GREEN, RED, BLUE, BLACK, YELLOW]
("-----------------------------------");
// Use allOf to create an enumSet containing all enumeration types, which internally initializes all enumeration instances according to the Class object
EnumSet<Color> enumSet1 = ();
("allOf Direct Fill: "+());// allOf Direct Fill: [GREEN, RED, BLUE, BLACK, YELLOW]
("-----------------------------------");
// The initial set consists of the elements of the specified range of enumeration values
EnumSet<Color> enumSet2= (,);
("Specify initialization range: "+()); //Specify initialization range: [BLACK, YELLOW]
("-----------------------------------");
//Specify the complementary set, that is, remove the elements in the parameter set from all enumeration types, as follows to remove the elements of the above enumSet2
EnumSet<Color> enumSet3= (enumSet2);
("Specify complement set: "+());// Specify complement set: [GREEN, RED, BLUE].
("-----------------------------------");
// Specify the element directly at initialization
EnumSet<Color> enumSet4= ().
("Specify element:" + ()); // Specify element: [BLACK]
EnumSet<Color> enumSet5= (,);
("Specify and element: "+());// Specify and element:[GREEN, BLACK]
("-----------------------------------");
//Copy the data of enumSet5 container as initialization data
EnumSet<Color> enumSet6= (enumSet5);; //Copy the data of enumSet5 container as initialization data.
("enumSet6: "+()); // enumSet6: [GREEN, BLACK]
("-----------------------------------");
List<Color> list = new ArrayList<Color>().
();
(); // duplicate elements
();
();
("list: "+());// list:[BLACK, BLACK, RED, BLUE]
// Use copyOf(Collection<E> c)
EnumSet enumSet7=(list).
("enumSet7: "+());// enumSet7:[RED, BLUE, BLACK]
}
}
source code analysis
initialization method
in order tononeOf
method as an example, take a look at theEnumSet
How it is initialized
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
// At this point, the universe contains all the enumeration values.
Enum<? >[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
// Determine what type of EnumSet object to initialize based on the size of the enumeration elements.
// Returns a RegularEnumSet if the length of the array is less than or equal to 64.
// RegularEnumSet is a subclass of EnumSet, the constructor of RegularEnumSet will call the constructor of EnumSet to save the enumeration type, enumeration array
if ( <= 64)
return new RegularEnumSet<>(elementType, universe);
else // otherwise return JumboEnumSet
return new JumboEnumSet<>(elementType, universe);
}
The noneOf method first gets all the enumeration values according to the enumeration type, then determines and initializes the corresponding EnumSet object according to the number of elements of the enumeration type.
RegularEnumSet uses a long object to store the elements of the EnumSet. Each bit of the long element represents an Enum value. Since the size of a long is 64 bits, it can store up to 64 different elements. JumboEnumSet uses an array of long elements to store the elements of the EnumSet. The only difference with RegularEnumSet is that theJumboEnumSet uses a long array to store the bit vectors, thus allowing for more than 64 values。
- The getUniverse method is as follows:
private static <E extends Enum<E>> E[] getUniverse(Class<E> elementType) {
return ()
.getEnumConstantsShared(elementType);
}
with respect to SharedSecrets,Java Here's how the official documentation describes it:A repository of “shared secrets”, which are a mechanism for calling implementation-private methods in another package without using reflection.
It means calling the mechanism that implements private methods in another package without using reflection. The ().getEnumConstantsShared method is used to get an array of enumeration elements of the specified type.
add method
Here is an example of RegularEnumSet to see the implementation details of the source code.
public boolean add(E e) {
// type check
typeCheck(e);
// Get the tagged value that stores the enumeration elements (the bits store the corresponding enumeration elements)
long oldElements = elements;
// Append new elements to the bits by | operations
elements |= (1L << ((Enum<? >)e).ordinal()); // If the enumeration element already exists, add the new element to the bit bits.
// Return false if the enumeration element already exists.
return elements ! = oldElements.
}
First the enumeration is type checked, then elements are received as an integer value of type long, as defined in RegularEnumSet:
/**
* Bit vector representation of this set. The 2^k bit indicates the
* presence of universe[k] in this set.
*
* The binary of the number if the bit bits are 1,then it means that there is an enumeration type element
*/
private long elements = 0L;
A long integer (binary) is used in RegularEnumSet to indicate whether an enumeration value has been added or removed. The key code for adding an enum element is this:elements |= (1L << ((Enum<?>)e).ordinal());
, below we show an example of how enumeration elements are added to a collection.
Take Season enumeration as an example, suppose there is no element added at this time, then the value of elements is 0. Now add element FAIL to the set, suppose the order of FAIL is 2, then the value of () is 2, 1 is shifted to the right by 2 bits and the value is 4, which is converted into binary and expressed as 0000 0100. If elements are expressed in binary and the value of a certain index is 1, then it means that there is an enumeration element at that position, then the enumeration element is () = index position. If elements is expressed in binary and a subscript (index) has a value of 1, then there is an enumeration element in that position, and the enumeration element is the one in the () = index position.
Another example to verify, assuming that the collection already has a FAIL element elements value of 4 (0000 0100), now add the element WINTER, assuming that the order of WINTER 3, () value of 3, 1 right shift 3 bit value of 8, converted to binary representation of 0000 1000, and elements or operation is (0000 1100). 1100). This means that there are two values, FAIL and WINTER.
remove method
public boolean remove(Object e) {
// in case of null,Direct return false
if (e == null)
return false;
Class<?> eClass = ();
// Type checking
if (eClass != elementType && () != elementType)
return false;
// Getting the tagged value
long oldElements = elements;
// commander-in-chief (military) remove on the bits corresponding to the elements of the 1 install 0
elements &= ~(1L << ((Enum<?>)e).ordinal());
return elements != oldElements;
}
Removing an element is simply a matter of replacing the 1 in the corresponding position with a 0.elements &= ~(1L << ((Enum<?>)e).ordinal());
This line of code executes this logic.
Assuming that we now have FAIL and WINTER in the set, the value of elements is 12 (0000 1100), and we now want to remove FAIL.(1L << ((Enum<?>)e).ordinal())
After the operation, the value is 4 (0000 0100), after the inverse, it is 1111 1011, and after the and operation with 0000 1100, it is 0000 1000, and the final result can be seen that the 1 in the corresponding position of FAIL becomes 0 after the operation.
EnumMap
In Article 37, it is proposed to replace the ordinal index with EnumMap.
37. Replacing ordinal indexes with EnumMap
Indexing arrays by ordinal numbers is not as good as using EnumMap, and should be used sparingly.ordinal()
。
For example this class represents a plant:
class Plant {
enum LifeCycle { ANNUAL, PERENNIAL, BIENNIAL }
final String name;
final LifeCycle lifeCycle;
Plant(String name, LifeCycle lifeCycle) {
= name;
= lifeCycle;
}
@Override public String toString() {
return name;
}
}
Suppose you have an array plantsByLifeCycle that represents all the plants in your garden and is used to list the plants by life cycle (annual, perennial, or biennial):
Some programmers implement this by putting these collections into an array indexed by type order number.
// generalized array
Set<Plant>[] plantsByLifeCycle =(Set<Plant>[]) new Set[().length];
for (int i = 0; i < ; i++)
plantsByLifeCycle[i] = new HashSet<>();
for (Plant p : garden)
// Using ordinal() to index into an array - DON'T DO THIS!
plantsByLifeCycle[()].add(p);
// Print the results
for (int i = 0; i < ; i++) {
("%s: %s%n",
()[i], plantsByLifeCycle[i]);
}
This technique has the following problems:
-
Arrays are not compatible with generics and require unchecked conversion.
-
Type Erase: Java's generics are implemented at compile time, meaning that generic type information is erased at runtime. In Java, on the other hand, an array is a covariant data structure with runtime type information. Since the generic type information is erased at runtime, there is no way to know the specific generic parameter type at runtime, which conflicts with the runtime type information of arrays.
-
Type Security: Java prohibits the creation of generic arrays in order to ensure type safety. Assuming that Java allows us to create generic arrays, the following scenario is possible:
List<String>[] stringLists = new List<String>[1]; Object[] objects = stringLists; objects[0] = new ArrayList<Integer>();
Here, it's a good idea to put a
ArrayList<Integer>
Assigned to aObject[]
references and did not raise any warnings or errors. However, nowstringLists[0]
is actually a list of Integers, and if we try to put a string in it, it throws the runtimeClassCastException
。
-
-
Arrays don't know what the index indicates and must be manually labeled for output.
-
int does not provide the type safety of an enumeration, and there is no way to check the correctness of an int value.
Solution:
-
utilization
:
// Using an EnumMap to associate data with an enum Map<, Set<Plant>> plantsByLifeCycle =new EnumMap<>(); for ( lc : ()) (lc, new HashSet<>()); for (Plant p : garden) ().add(p); (plantsByLifeCycle);
This program is shorter, clearer, safer, and about as fast as the original version. The reason for the comparable speed is that EnumMap uses such arrays internally, but it hides the implementation details from the programmer.
-
Using streams can further shorten the program:
// Naive stream-based approach - unlikely to produce an EnumMap! ((garden).collect(groupingBy(p -> )));
The performance of this code is poor because the underlying layer is not based on EnumMap, but implements Map itself.
-
To improve performance, you can specify a Map implementation using the mapFactory parameter:
// Using a stream and an EnumMap to associate data with an enum ( (garden).collect(groupingBy(p -> ,() -> new EnumMap<>(), toSet())) );
The following example describes the loading between the gas-liquid-solid three units in a two-dimensional array and reads the values of the two-dimensional array using the ordinal index:
// Using ordinal() to index array of arrays - DON'T DO THIS!
public enum Phase {
SOLID, LIQUID, GAS;
public enum Transition {
MELT, FREEZE, BOIL, CONDENSE, SUBLIME, DEPOSIT;
// Rows indexed by from-ordinal, cols by to-ordinal
private static final Transition[][] TRANSITIONS = {
{ null, MELT, SUBLIME },
{ FREEZE, null, BOIL },
{ DEPOSIT, CONDENSE, null }
};
// Returns the phase transition from one phase to another
public static Transition from(Phase from, Phase to) {
return TRANSITIONS[()][()];
}
}
}
The problem with this program is the same as the previous garden example, the compiler cannot know the relationship between the ordinal number and the array index. If you make a mistake in the conversion table, or if you modify Phase or Forget to update when enumerating types and the program will fail at runtime.
You can do better with EnumMap:
// Using a nested EnumMap to associate data with enum pairs
public enum Phase {
SOLID, LIQUID, GAS;
public enum Transition {
MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID);
private final Phase from;
private final Phase to;
Transition(Phase from, Phase to) {
= from;
= to;
}
// Initialize the phase transition map
private static final Map<Phase, Map<Phase,Transition> m =
new EnumMap<Phase, Map<Phase ,Transition>>();
static{
for (Phase p : Phase. values())
(p,new EnumMap<Phase,Transition ());
for (Transition trans : () )
(trans. src).put(, trans) ;
}
public static Transition from(Phase src, Phase dst) {
return (src).get(dst);
}
}
If you want to add a new phase to the system: plasma. There are only two changes to this phase: ionization, and deionization. Modifying the EnumMap-based version is much easier and clearer and safer than the array-based version:
// Adding a new phase using the nested EnumMap implementation
public enum Phase {
SOLID, LIQUID, GAS, PLASMA;
public enum Transition {
MELT(SOLID, LIQUID), FREEZE(LIQUID, SOLID),
BOIL(LIQUID, GAS), CONDENSE(GAS, LIQUID),
SUBLIME(SOLID, GAS), DEPOSIT(GAS, SOLID),
IONIZE(GAS, PLASMA), DEIONIZE(PLASMA, GAS);
... // Remainder unchanged
}
}
So how is EnumMap used and what does the underlying implementation look like?
basic usage
First think about this problem, now there are a bunch of size of the same size and different colors of data, you need to count the number of each color is how much in order to enter the data into the warehouse, define the following enumeration is used to represent the color Color:.
enum Color {
GREEN,RED,BLUE,YELLOW
}
public class EnumMapDemo {
public static void main(String[] args){
List<Clothes> list = new ArrayList<>();
(new Clothes("C001",));
(new Clothes("C002",));
(new Clothes("C003",));
(new Clothes("C004",));
(new Clothes("C005",));
(new Clothes("C006",));
(new Clothes("C007",));
(new Clothes("C008",));
(new Clothes("C009",));
(new Clothes("C010",));
//programmatic1:utilizationHashMap
Map<String,Integer> map = new HashMap<>();
for (Clothes clothes:list){
String colorName=().name();
Integer count = (colorName);
if(count!=null){
(colorName,count+1);
}else {
(colorName,1);
}
}
(());
("---------------");
//programmatic2:utilizationEnumMap
Map<Color,Integer> enumMap=new EnumMap<>();
for (Clothes clothes:list){
Color color=();
Integer count = (color);
if(count!=null){
(color,count+1);
}else {
(color,1);
}
}
(());
}
/**
output result:
{RED=2, BLUE=3, YELLOW=3, GREEN=2}
---------------
{GREEN=2, RED=2, BLUE=3, YELLOW=3}
*/
}
The code is relatively simple, we use two solutions, a HashMap, an EnumMap, although both counted the correct results, but EnumMap as the exclusive collection of enumeration, we have no reason to use HashMap again, after allEnumMap requires that its Key must be of type Enumand thus using the Color enumeration instance as the key is most appropriate and avoids the step of getting the name.
What's more, EnumMap is more efficient, because its internal is realized through the array (analyzed later), note that the key value of EnumMap can not be null, although it is an exclusive collection of enumeration, but its operation with the general Map is almost the same, in a nutshell EnumMap is specifically tailored for the type of enumeration of the implementation of the Map, although the use of other Map (eg. HashMap) can accomplish the same function, but the use of EnumMap will be more efficient, it can only receive the same enumeration type instances as the key value and can not be null, due to the enumeration type instances of the number of relatively fixed and limited, so the EnumMap using an array to store and enumeration type corresponding to the value of the array, after all, is a section of the array is a continuous memory space, according to the procedure of localization According to the principle of program locality, the efficiency will be quite high.
Let's take a closer look at the usage of EnumMap, starting with the constructor:
// Create an empty enum map with the specified key type.
EnumMap(Class<K> keyType)
// Create an enum map whose key type is the same as the specified enum map, initially containing the same mapping relationship (if any).
EnumMap(EnumMap<K,? extends V> m)
// Create an enumeration map, initializing it from the specified mapping.
EnumMap(Map<K,? extends V> m)
Unlike HashMap, it needs to pass a type information, that is, Class object, through this parameter EnumMap can be initialized according to the type information of its internal data structure, the other two constructor initialization passed into a Map collection, the code demonstrates the following:
// Use the first construct
Map<Color,Integer> enumMap=new EnumMap<>();
//Use the second construct
Map<Color,Integer> enumMap2=new EnumMap<>(enumMap);
//Use the third construct
Map<Color,Integer> hashMap = new HashMap<> ();
(, 2);
(, 3);
Map<Color, Integer> enumMap = new EnumMap<>(hashMap);
As for the method of EnumMap, with the ordinary map almost no difference, note that the main difference with the HashMap is that the constructor needs to pass the type parameter and EnumMap to ensure that the order of Key and the order of the enumeration is the same, but please remember that the Key can not be null.
source code analysis
EnumMap source code part of our main analysis of its internal storage structure, add the implementation of the lookup, understand these points, corresponding to the principle of the internal implementation of EnumMap is also clearer
Data Structures and Constructors
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V> implements , Cloneable{
// keytypes of,beClassobject reference
private final Class<K> keyType;
// stockpileKeyArray of values
private transient K[] keyUniverse;
// stockpileValueArray of values
private transient Object[] vals;
// map(used form a nominal expression)size
private transient int size = 0;
// unoccupiedmap
private static final Enum<?>[] ZERO_LENGTH_ENUM_ARRAY = new Enum<?>[0];
// constructor
public EnumMap(Class<K> keyType) {
= keyType;
keyUniverse = getKeyUniverse(keyType);
vals = new Object[];
}
}
EnumMap inherits the AbstractMap class, so EnumMap has the general use of map.
in the constructor via thekeyUniverse = getKeyUniverse(keyType);
Initializes the keyUniverse array of values, which internally stores all possible enumeration values, and then initializes the presenceValue worth array vals, which is the same size as the number of enumeration instances, the getKeyUniverse method is implemented as follows
// Return an array of enumerations
private static <K extends Enum<K>> K[] getKeyUniverse(Class<K> keyType) {
// The final call to the values method of the enumeration type, which returns all possible values of the enumeration.
return ()
.getEnumConstantsShared(keyType);
}
Looking at the return value of the method, the return type is an enumerated array; obviously, the final return value is exactly the return value of the values method of the enumerated type in theenumerationIn the chapter we analyzed the values method to return all possible enumeration values, so the keyUniverse array stores all possible enumeration values of the enumeration type.
put method
public V put(K key, V value) {
typeCheck(key);//sensingkeytypes of
//Getting storagevalueWorth array subscripts
int index = ();
//Getting old values
Object oldValue = vals[index];
//set upvalue(be) worth
vals[index] = maskNull(value);
if (oldValue == null)
size++;
return unmaskNull(oldValue);//返回旧(be) worth
}
The typeCheck method performs key type checking to determine if the type is an enumeration, and throws an exception if the type is incorrect.
private void typeCheck(K key) {
Class<?> keyClass = ();//Getting type information
if (keyClass != keyType && () != keyType)
throw new ClassCastException(keyClass + " != " + keyType);
}
Then byint index = ()
The enumeration instance's sequential value is obtained by using this value as a subscript and storing the value in the element of the vals array corresponding to the subscript i.e.vals[index]
, which is why EnumMap maintains the same storage order as enumeration instances.
The maskNull and unmaskNull methods are called when assigning a value to an element in vals[] and returning the old value, respectively
//in the name ofNULLWorth empty object instances
private static final Object NULL = new Object() {
public int hashCode() {
return 0;
}
public String toString() {
return "";
}
};
private Object maskNull(Object value) {
//If the value is null,come (or go) backNULLboyfriend,否则come (or go) backvalue
return (value == null ? NULL : value);
}
@SuppressWarnings("unchecked")
private V unmaskNull(Object value) {
//commander-in-chief (military)NULLboyfriend转换为null(be) worth
return (V)(value == NULL ? null : value);
}
It seems that EnumMap still allows the storage of null values, but the key can never be null, for the null value, EnumMap special treatment, packing it as a NULL object, after all, vals [] stored Object, maskNull method and unmaskNull method is exactly used for null packing and unpacking of the The maskNull method and the unmaskNull method are used for wrapping and unwrapping.
get method
public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
}
//treat (sb a certain way)KeyThe validity of the value and type information is judged
private boolean isValidKey(Object key) {
if (key == null)
return false;
// Cheaper than instanceof Enum followed by getDeclaringClass
Class<?> keyClass = ();
return keyClass == keyType || () == keyType;
}
Compared to the put method, the get method is quite concise. If the key is valid, the index is taken directly from the ordinal method, and then the value is returned by the index in the value array vals.
remove method
public V remove(Object key) {
//judgementskeyIs the value valid
if (!isValidKey(key))
return null;
//Direct access to the index
int index = ((Enum<?>)key).ordinal();
Object oldValue = vals[index];
//The corresponding subscript element value is set tonull
vals[index] = null;
if (oldValue != null)
size--;//diminishsize
return unmaskNull(oldValue);
}
Very simple, key value is valid, get subscript index value by key, set vals[] corresponding subscript value to null, size minus one.
contains method
//Determine whether or not it contains avalue
public boolean containsValue(Object value) {
value = maskNull(value);
//Iterative array implementation
for (Object val : vals)
if ((val))
return true;
return false;
}
//Determine if it containskey
public boolean containsKey(Object key) {
return isValidKey(key) && vals[((Enum<?>)key).ordinal()] != null;
}
wrap-up
This is the main principle of the implementation of EnumMap, that is, there are two internal arrays of the same length, one represents all the possible keys (enumerated values), one represents the corresponding value, does not allow the keyynull, but allows the value to be null, the keys have a corresponding index, according to the index to directly access and manipulate their key arrays and the value arrays, due to the operation of the arrays, and therefore very efficient.
About the Author.
From the first-line programmer Seven's exploration and practice, continuous learning iteration in the~
This article is included in my personal blog:https://
Public number: seven97, welcome to follow~