用于枚舉類(lèi)型鍵的專(zhuān)用Map實(shí)現(xiàn)琐凭。 EnumMap映射中的所有鍵必須來(lái)自創(chuàng)建映射時(shí)顯式或隱式指定的單個(gè)枚舉類(lèi)型。 枚舉映射在內(nèi)部表示為數(shù)組饶号, 枚舉映射按其鍵的自然順序(枚舉常量的聲明順序)維護(hù)撞叽。 這反映在集合視圖keySet(),entrySet()和values()返回的迭代器中也祠。集合視圖返回的迭代器非常一致:它們永遠(yuǎn)不會(huì)拋出ConcurrentModificationException昙楚,它們可能會(huì)也可能不會(huì)顯示出迭代進(jìn)行過(guò)程中對(duì)映射所做的修改的而對(duì)其造成的影響。不允許使用空鍵诈嘿,嘗試插入空鍵將拋出NullPointerException堪旧。但是,測(cè)試是否存在空鍵或刪除空鍵將正常運(yùn)行奖亚,且允許空值淳梦。同大多數(shù)集合一樣,EnumMap不是線(xiàn)程同步的昔字。
定義及說(shuō)明
定義如下:
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>
implements java.io.Serializable, Cloneable{}
1爆袍、繼承了AbstractMap,即擁有Map基本的操作作郭。
2陨囊、實(shí)現(xiàn)了java.io.Serializable,即可序列化和反序列化
3夹攒、實(shí)現(xiàn)了Cloneable谆扎,即支持clone
我們?cè)倏匆幌滤臉?gòu)造方法:
//此映射的所有鍵的枚舉類(lèi)型的Class對(duì)象
private final Class<K> keyType;
//包含在keyType中的所有枚舉常量(用來(lái)緩存性能)
private transient K[] keyUniverse
//此映射的數(shù)組表示形式。
private transient Object[] vals;
//Map大小
private transient int size = 0;
//創(chuàng)建具有指定鍵類(lèi)型的空枚舉映射
public EnumMap(Class<K> keyType) {
//指定此EnumMap的key的枚舉類(lèi)型的Class對(duì)象
this.keyType = keyType;
//返回包含在keyType中的所有值枚舉常量
keyUniverse = getKeyUniverse(keyType);
//根據(jù)KeyType中的枚舉常量的個(gè)數(shù)初始化vals數(shù)組
vals = new Object[keyUniverse.length];
}
//創(chuàng)建一個(gè)與m相同的Map
public EnumMap(EnumMap<K, ? extends V> m) {
keyType = m.keyType;
keyUniverse = m.keyUniverse;
vals = m.vals.clone();
size = m.size;
}
//創(chuàng)建從指定Map初始化的枚舉映射芹助。 如果指定的映射是EnumMap實(shí)例堂湖,則此構(gòu)造函數(shù)的行為與EnumMap(EnumMap)相同闲先。 否則,指定的映射必須至少包含一個(gè)映射(以確定新的枚舉映射的鍵類(lèi)型)
public EnumMap(Map<K, ? extends V> m) {
//判斷是否為EnumMap類(lèi)型
if (m instanceof EnumMap) {
EnumMap<K, ? extends V> em = (EnumMap<K, ? extends V>) m;
keyType = em.keyType;
keyUniverse = em.keyUniverse;
vals = em.vals.clone();
size = em.size;
} else {
//不是Enum類(lèi)型
if (m.isEmpty())
throw new IllegalArgumentException("Specified map is empty");
keyType = m.keySet().iterator().next().getDeclaringClass();
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
putAll(m);
}
}
需要注意:
1无蜂、keyUniverse數(shù)組中包含的元素伺糠,是keyType枚舉類(lèi)型中所有的枚舉常量。且vals數(shù)組長(zhǎng)度與keyUniverse數(shù)組長(zhǎng)度相同斥季。
2训桶、使用第三個(gè)構(gòu)造函數(shù)創(chuàng)建EnumMap的時(shí)候,如果指定的映射是EnumMap實(shí)例酣倾,則此構(gòu)造函數(shù)的行為與第二個(gè)構(gòu)造函數(shù)相同舵揭。 否則,指定的Map中必須至少包含一個(gè)映射躁锡,用來(lái)確定新的枚舉映射的鍵類(lèi)型午绳。
源碼簡(jiǎn)析
老規(guī)矩,看put()方法:
public V put(K key, V value) {
//檢查key是不是正確類(lèi)型
typeCheck(key);
//獲取key在枚舉常量的聲明順序(第幾個(gè)聲明的)
int index = key.ordinal();
//獲取在當(dāng)前index位置的值
Object oldValue = vals[index];
//判斷value是否為空映之,并賦值
vals[index] = maskNull(value);
//判斷oldValue是否為空拦焚,已確定是添加了元素,還是修改了值
if (oldValue == null)
//如果是添加了元素杠输,則數(shù)組長(zhǎng)度加1
size++;
//根據(jù)value的值判斷value是否為空赎败,并返回
return unmaskNull(oldValue);
}
//如果key不是此枚舉集的正確類(lèi)型,則引發(fā)異常
private void typeCheck(K key) {
Class<?> keyClass = key.getClass();
if (keyClass != keyType && keyClass.getSuperclass() != keyType)
throw new ClassCastException(keyClass + " != " + keyType);
}
//根據(jù)value是否為空給其賦值
private Object maskNull(Object value) {
return (value == null ? NULL : value);
}
//根據(jù)value的值判斷其是否為空
@SuppressWarnings("unchecked")
private V unmaskNull(Object value) {
return (V)(value == NULL ? null : value);
}
由上面的分析我們得出蠢甲,假如i為數(shù)組keyUniverse的某個(gè)角標(biāo)僵刮,則keyUniverse[i]在數(shù)組EnumMap中所對(duì)應(yīng)的值為vals[i]。而且鹦牛,EnumMap的鍵值對(duì)的映射是keyUniverse數(shù)組與vals數(shù)組中相同位置的元素的映射妓笙。
為什么TreeMap永遠(yuǎn)不會(huì)拋出ConcurrentModificationException ?請(qǐng)結(jié)合fail-fast想一想
好了能岩,本篇文章到此結(jié)束寞宫。