JDK源码笔记2-EnumSet/EnumMap
1.
EnumSet
示例及核心源码分析
2. EnumMap 示例及核心源码分析
package
com.landon.mavs.example.collection;
import java.util.EnumSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.landon.mavs.example.collection.EnumMapExample.NBA;
/** */ /**
*
* EnumSet example
*
* <pre>
* 1.public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
* implements Cloneable, java.io.Serializable
* 2.其内部元素为枚举,另外可以看到其是一个抽象类abstract
* 3.其在内部表示为位向量,足以用作传统上基于 int 的“位标志”的替换形式.(即类似0x1000000,按bit存储,用位运算进行相关操作)
* 4. 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);
* }
* 从源码上看:即根据传入的枚举类型判断组成长度,64以内则返回RegularEnumSet,否则JumboEnumSet
* 5.从其内部API方法看,全部是静态方法static.
* 6.以RegularEnumSet内部方法实现:
* public boolean add(E e) {
* typeCheck(e);
* // elements默认为0
* long oldElements = elements;
* elements |= (1L << ((Enum)e).ordinal());
* return elements != oldElements;
* }
* 从add源码上看:1.取枚举值的ordinal,初始为0. 2.1 << ordinal 3.与elements做|运算
* 举例:a.添加ordinal为0,则计算后elements为1
* b.添加ordinal为1,则计算后elements为(10 | 01) = 11
* c.添加ordinal为2,则计算后elements为(011 | 100) = 111
* ->所以从源码上看,其就是用一个long来存储枚举.你懂得(long是64位).
*
* public boolean contains(Object e) {
* if (e == null)
* return false;
* Class eClass = e.getClass();
* if (eClass != elementType && eClass.getSuperclass() != elementType)
* return false;
*
* return (elements & (1L << ((Enum)e).ordinal())) != 0;
* }
* 从contains源码上看:最重要的是最好一句:(elements & (1L << ((Enum)e).ordinal())) != 0
* 1.1L << ((Enum)e).ordinal()
* 2.与elements做&运算
* 举例:如果ordinal为2,则通过第一步计算值为4(100) & 111(之前已经添加过ordinal为2的元素,高位至1)
* ->则高位肯定为1,则表示有这个元素
* 总结:利用一个long和位运算实现EnumSet的快速存储和判断.
* 7.至于JumboEnumSet的内部实现:则是用一个long elements[]实现,只是对long的扩展,其实现细节差不太多,这里不详述了
* </pre>
*
* @author landon
*
*/
public class EnumSetExample {
private static final Logger LOGGER = LoggerFactory
.getLogger(EnumSetExample.class);
public static void main(String[] args) {
// allOf:一个包含指定元素类型的所有元素的枚举 set
EnumSet<NBA> allofEnumSet = EnumSet.allOf(NBA.class);
// [allofEnumSet:[MAVS, LAKERS, PACERS]]
LOGGER.debug("allofEnumSet:{}", allofEnumSet);
// noneOf:创建一个具有指定元素类型的空枚举 set
EnumSet<NBA> noneofEnumSet = EnumSet.noneOf(NBA.class);
// 添加一个元素
noneofEnumSet.add(NBA.LAKERS);
// [noneofEnumSet:[LAKERS]]
LOGGER.debug("noneofEnumSet:{}", noneofEnumSet);
// complementOf:取补
EnumSet<NBA> complementOfEnumSet = EnumSet.complementOf(noneofEnumSet);
// [[complementOfEnumSet:[MAVS, PACERS]]
LOGGER.debug("complementOfEnumSet:{}", complementOfEnumSet);
// copyof:拷贝
EnumSet<NBA> copyofEnumSet = EnumSet.copyOf(complementOfEnumSet);
// [copyofEnumSet:[MAVS, PACERS]]
LOGGER.debug("copyofEnumSet:{}", copyofEnumSet);
// of(E e):最初包含指定元素的枚举 set
EnumSet<NBA> ofEnumSet = EnumSet.of(NBA.PACERS);
LOGGER.debug("ofEnumSet:{}", ofEnumSet);
// of(E first,E rest)
NBA[] nbas = new NBA[] { NBA.LAKERS, NBA.MAVS, NBA.PACERS };
EnumSet<NBA> ofEnumSet2 = EnumSet.of(NBA.PACERS, nbas);
// 从输出可以可以看到:是按照枚举的ordinal顺序输出的
LOGGER.debug("ofEnumSet2:{}", ofEnumSet2);
// range(E from, E to) [from,to]
EnumSet<NBA> rangeEnumSet = EnumSet.range(NBA.MAVS, NBA.PACERS);
LOGGER.debug("rangeEnumSet:{}", rangeEnumSet);
// Exception in thread "main" java.lang.IllegalArgumentException: PACERS
// > LAKERS
// 抛出了异常,所以from和to的顺序不能颠倒(按照枚举的ordinal顺序)
EnumSet<NBA> rangeEnumSet2 = EnumSet.range(NBA.PACERS, NBA.LAKERS);
LOGGER.debug("rangeEnumSet2:{}", rangeEnumSet2);
}
}
import java.util.EnumSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.landon.mavs.example.collection.EnumMapExample.NBA;
/** */ /**
*
* EnumSet example
*
* <pre>
* 1.public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
* implements Cloneable, java.io.Serializable
* 2.其内部元素为枚举,另外可以看到其是一个抽象类abstract
* 3.其在内部表示为位向量,足以用作传统上基于 int 的“位标志”的替换形式.(即类似0x1000000,按bit存储,用位运算进行相关操作)
* 4. 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);
* }
* 从源码上看:即根据传入的枚举类型判断组成长度,64以内则返回RegularEnumSet,否则JumboEnumSet
* 5.从其内部API方法看,全部是静态方法static.
* 6.以RegularEnumSet内部方法实现:
* public boolean add(E e) {
* typeCheck(e);
* // elements默认为0
* long oldElements = elements;
* elements |= (1L << ((Enum)e).ordinal());
* return elements != oldElements;
* }
* 从add源码上看:1.取枚举值的ordinal,初始为0. 2.1 << ordinal 3.与elements做|运算
* 举例:a.添加ordinal为0,则计算后elements为1
* b.添加ordinal为1,则计算后elements为(10 | 01) = 11
* c.添加ordinal为2,则计算后elements为(011 | 100) = 111
* ->所以从源码上看,其就是用一个long来存储枚举.你懂得(long是64位).
*
* public boolean contains(Object e) {
* if (e == null)
* return false;
* Class eClass = e.getClass();
* if (eClass != elementType && eClass.getSuperclass() != elementType)
* return false;
*
* return (elements & (1L << ((Enum)e).ordinal())) != 0;
* }
* 从contains源码上看:最重要的是最好一句:(elements & (1L << ((Enum)e).ordinal())) != 0
* 1.1L << ((Enum)e).ordinal()
* 2.与elements做&运算
* 举例:如果ordinal为2,则通过第一步计算值为4(100) & 111(之前已经添加过ordinal为2的元素,高位至1)
* ->则高位肯定为1,则表示有这个元素
* 总结:利用一个long和位运算实现EnumSet的快速存储和判断.
* 7.至于JumboEnumSet的内部实现:则是用一个long elements[]实现,只是对long的扩展,其实现细节差不太多,这里不详述了
* </pre>
*
* @author landon
*
*/
public class EnumSetExample {
private static final Logger LOGGER = LoggerFactory
.getLogger(EnumSetExample.class);
public static void main(String[] args) {
// allOf:一个包含指定元素类型的所有元素的枚举 set
EnumSet<NBA> allofEnumSet = EnumSet.allOf(NBA.class);
// [allofEnumSet:[MAVS, LAKERS, PACERS]]
LOGGER.debug("allofEnumSet:{}", allofEnumSet);
// noneOf:创建一个具有指定元素类型的空枚举 set
EnumSet<NBA> noneofEnumSet = EnumSet.noneOf(NBA.class);
// 添加一个元素
noneofEnumSet.add(NBA.LAKERS);
// [noneofEnumSet:[LAKERS]]
LOGGER.debug("noneofEnumSet:{}", noneofEnumSet);
// complementOf:取补
EnumSet<NBA> complementOfEnumSet = EnumSet.complementOf(noneofEnumSet);
// [[complementOfEnumSet:[MAVS, PACERS]]
LOGGER.debug("complementOfEnumSet:{}", complementOfEnumSet);
// copyof:拷贝
EnumSet<NBA> copyofEnumSet = EnumSet.copyOf(complementOfEnumSet);
// [copyofEnumSet:[MAVS, PACERS]]
LOGGER.debug("copyofEnumSet:{}", copyofEnumSet);
// of(E e):最初包含指定元素的枚举 set
EnumSet<NBA> ofEnumSet = EnumSet.of(NBA.PACERS);
LOGGER.debug("ofEnumSet:{}", ofEnumSet);
// of(E first,E rest)
NBA[] nbas = new NBA[] { NBA.LAKERS, NBA.MAVS, NBA.PACERS };
EnumSet<NBA> ofEnumSet2 = EnumSet.of(NBA.PACERS, nbas);
// 从输出可以可以看到:是按照枚举的ordinal顺序输出的
LOGGER.debug("ofEnumSet2:{}", ofEnumSet2);
// range(E from, E to) [from,to]
EnumSet<NBA> rangeEnumSet = EnumSet.range(NBA.MAVS, NBA.PACERS);
LOGGER.debug("rangeEnumSet:{}", rangeEnumSet);
// Exception in thread "main" java.lang.IllegalArgumentException: PACERS
// > LAKERS
// 抛出了异常,所以from和to的顺序不能颠倒(按照枚举的ordinal顺序)
EnumSet<NBA> rangeEnumSet2 = EnumSet.range(NBA.PACERS, NBA.LAKERS);
LOGGER.debug("rangeEnumSet2:{}", rangeEnumSet2);
}
}
2. EnumMap 示例及核心源码分析
package
com.landon.mavs.example.collection;
import java.util.Collection;
import java.util.EnumMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** */ /**
*
* EnumMap example
*
* <pre>
* 1.public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
* implements java.io.Serializable, Cloneable
* 2.可见EnumMap的key是一个枚举
* 3.枚举映射在内部表示为数组
* 4. public V put(K key, V value) {
* typeCheck(key);
*
* int index = key.ordinal();
* Object oldValue = vals[index];
* vals[index] = maskNull(value);
* if (oldValue == null)
* size++;
* return unmaskNull(oldValue);
* }
* 从put的源码可以看到,是利用Enum的ordinal作为数组的索引.所以实现紧凑且高效
* </pre>
*
* @author landon
*
*/
public class EnumMapExample {
private static final Logger LOGGER = LoggerFactory
.getLogger(EnumMapExample.class);
/** *//**
*
* Nba球队枚举,分别是小牛,湖人,步行者
*
* @author landon
*
*/
static enum NBA {
MAVS, LAKERS, PACERS,
}
public static void main(String[] args) {
// 构造函数public EnumMap(Class<K> keyType),参数表示key类型
// 泛型只是编译起作用
EnumMap<NBA, String> em = new EnumMap<NBA, String>(NBA.class);
// put顺序不是根据枚举的ordinal顺序
em.put(NBA.LAKERS, "kobe");
em.put(NBA.MAVS, "dirk");
em.put(NBA.PACERS, "miller");
// get方法会首先检查参数的class是否valid(与keyTypeClass对比)
LOGGER.debug("mavs_player:{}", em.get(NBA.MAVS));
// 类型检查失败则返回null
LOGGER.debug("mavs_player:{}", em.get("mavs"));
Collection<String> values = em.values();
// 从输出可以看到,视图的内部顺序是枚举定义的顺序.
// 输出em.values:[dirk, kobe, miller]
LOGGER.debug("em.values:{}", values.toString());
// 抛出空指针异常Exception in thread "main" java.lang.NullPointerException
// 所以key不允许为null
em.put(null, "tmac");
}
}
import java.util.Collection;
import java.util.EnumMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** */ /**
*
* EnumMap example
*
* <pre>
* 1.public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
* implements java.io.Serializable, Cloneable
* 2.可见EnumMap的key是一个枚举
* 3.枚举映射在内部表示为数组
* 4. public V put(K key, V value) {
* typeCheck(key);
*
* int index = key.ordinal();
* Object oldValue = vals[index];
* vals[index] = maskNull(value);
* if (oldValue == null)
* size++;
* return unmaskNull(oldValue);
* }
* 从put的源码可以看到,是利用Enum的ordinal作为数组的索引.所以实现紧凑且高效
* </pre>
*
* @author landon
*
*/
public class EnumMapExample {
private static final Logger LOGGER = LoggerFactory
.getLogger(EnumMapExample.class);
/** *//**
*
* Nba球队枚举,分别是小牛,湖人,步行者
*
* @author landon
*
*/
static enum NBA {
MAVS, LAKERS, PACERS,
}
public static void main(String[] args) {
// 构造函数public EnumMap(Class<K> keyType),参数表示key类型
// 泛型只是编译起作用
EnumMap<NBA, String> em = new EnumMap<NBA, String>(NBA.class);
// put顺序不是根据枚举的ordinal顺序
em.put(NBA.LAKERS, "kobe");
em.put(NBA.MAVS, "dirk");
em.put(NBA.PACERS, "miller");
// get方法会首先检查参数的class是否valid(与keyTypeClass对比)
LOGGER.debug("mavs_player:{}", em.get(NBA.MAVS));
// 类型检查失败则返回null
LOGGER.debug("mavs_player:{}", em.get("mavs"));
Collection<String> values = em.values();
// 从输出可以看到,视图的内部顺序是枚举定义的顺序.
// 输出em.values:[dirk, kobe, miller]
LOGGER.debug("em.values:{}", values.toString());
// 抛出空指针异常Exception in thread "main" java.lang.NullPointerException
// 所以key不允许为null
em.put(null, "tmac");
}
}