Java集合框架:EnumMap
EnumMap定義
package java.util;import java.util.Map.Entry;
import sun.misc.SharedSecrets;
public class EnumMap<K extends Enum<K>, V> extends AbstractMap<K, V>implements java.io.Serializable, Cloneable{private final Class<K> keyType;private transient K[] keyUniverse;private transient Object[] vals;private transient int size = 0;
} ??keyType變量是EnumMap的key泛型的類對(duì)象,EnumMap依據(jù)這個(gè)類型。能夠獲得keyUniverse的內(nèi)容。vals存放的是與keyUniverse映射的值。假設(shè)沒(méi)有映射則為null,假設(shè)映射為null則會(huì)特殊處理成NULL。NULL的定義例如以下:
private static final Object NULL = new Object() {public int hashCode() {return 0;}public String toString() {return "java.util.EnumMap.NULL";}}; ??對(duì)于值NULL的處理相似WeakHashMap的特殊處理,會(huì)有兩個(gè)方法:
private Object maskNull(Object value) {return (value == null ? NULL : value);}private V unmaskNull(Object value) {return (V) (value == NULL ? null : value);} ??這樣能夠區(qū)分vals中是null(即沒(méi)有映射)還是NULL(即映射為null);
??EnumMap的size是依據(jù)vals中的非null(包含NULL)的值的個(gè)數(shù)確定的,比方put方法:
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);} ??typeCheck推斷key的類對(duì)象或者父類對(duì)象是否與keyType相等,假設(shè)不相等則拋出ClassCastException異常。
??注意EnumMap并沒(méi)有相似HashMap的resize的過(guò)程,也沒(méi)有載入因子的概念,由于在一個(gè)EnumMap創(chuàng)建的時(shí)候,keyUniverse和vals的大小就固定。
EnumMap使用
??先舉個(gè)小樣例:
package collections.map;import java.util.EnumMap;
import java.util.Map;public class EnumMapTest
{public enum Color{RED,BLUE,BLACK,YELLOW,GREEN;}public static void main(String[] args){EnumMap<Color,String> map = new EnumMap<>(Color.class);EnumMap<Color,String> map = new EnumMap<>(Color.class);map.put(Color.YELLOW, "黃色");map.put(Color.RED, "紅色");map.put(Color.BLUE, null);
// map.put(null, "無(wú)"); //會(huì)報(bào)NullPonitException的錯(cuò)誤map.put(Color.BLACK, "黑色");map.put(Color.GREEN, "綠色");for(Map.Entry<Color,String> entry:map.entrySet()){System.out.println(entry.getKey()+":"+entry.getValue());}System.out.println(map);}
} ??執(zhí)行結(jié)果:
RED:紅色
BLUE:null
BLACK:黑色
YELLOW:黃色
GREEN:綠色
{RED=紅色, BLUE=null, BLACK=黑色, YELLOW=黃色, GREEN=綠色} ??EnumMap的key不同意為null,value能夠?yàn)閚ull,依照key在enum中的順序進(jìn)行保存。非線程安全。能夠用工具類Collections進(jìn)行包裝成線程安全的:
Map<EnumKey, V> m = Collections.synchronizedMap(new EnumMap<EnumKey, V>(...)); ??有關(guān)enum的應(yīng)用知識(shí)能夠參考《Java枚舉類型enum》。
??EnumMap的基本操作都比較快,都在常量時(shí)間內(nèi)完畢,基本上(但不保證)比HashMap快。
??EnumMap有三個(gè)構(gòu)造函數(shù):
- public EnumMap(Class<K> keyType);
- public EnumMap(EnumMap<K, ?
extends V> m);
- public EnumMap(Map<K, ?
extends V> m) ;
??前兩個(gè)構(gòu)造函數(shù)一目了然,對(duì)第三個(gè)構(gòu)造函數(shù)進(jìn)行分析:
Map<Integer,Integer> map1 = new HashMap<>();map1.put(1, 1);map1.put(3, 3);map1.put(2, 2);Map<Integer,Integer> map2 = new EnumMap<>(map1);//編譯器提示錯(cuò)誤:Cannot infer type arguments for EnumMap<> ??這個(gè)是由于Integer并非extends Enum;
??這里變換一下,採(cǎi)用Map
Map<Enum,Integer> map1 = new HashMap<>();map1.put(Color.YELLOW, 1);map1.put(Color.RED, 3);map1.put(Color.BLUE, 2);Map<Enum,Integer> map2 = new EnumMap<>(map1);for(Map.Entry entry:map2.entrySet()){System.out.println(entry.getKey()+":"+entry.getValue());}System.out.println(map2);System.out.println(map2.size()); ??能夠正常執(zhí)行。輸出結(jié)果:
RED:3
BLUE:2
YELLOW:1
{RED=3, BLUE=2, YELLOW=1}
3 ??相信大家能夠總結(jié)個(gè)一二了吧。
EnumMap用途
??《Effective Java》中作者建議用EnumMap取代敘述索引。最好不要用序數(shù)來(lái)索引數(shù)組,而要使用EnumMap。
??這里採(cǎi)用《Effective Java》書(shū)中的樣例來(lái)舉例。
public static class Herb{public enum Type{ANNUAL, PERENNIAL, BIENNTAL}private final String name;private final Type type;public Herb(String name, Type type){this.name = name;this.type = type;}public Type getType(){return type;}@Overridepublic String toString(){return name;}} ??如今用一座種滿香草的花園,想要依照類型(一年生、多年生、兩年生,即上面Type的類型)進(jìn)行組織之后將這些植物列出來(lái)。假設(shè)使用數(shù)組實(shí)現(xiàn)的話。須要構(gòu)建三個(gè)集合,每種類型一個(gè)。而且遍歷整座花園,將每種香草放到相應(yīng)的集合中。
Herb[] garden = new Herb[]{new Herb("f1",Herb.Type.ANNUAL),new Herb("f2",Herb.Type.PERENNIAL),new Herb("f3",Herb.Type.BIENNTAL),new Herb("f4",Herb.Type.PERENNIAL),new Herb("f5",Herb.Type.ANNUAL),new Herb("f6",Herb.Type.BIENNTAL),new Herb("f7",Herb.Type.ANNUAL),new Herb("f8",Herb.Type.BIENNTAL),new Herb("f9",Herb.Type.PERENNIAL)};Set<Herb>[] herbsByType = (Set<Herb>[]) new Set[Herb.Type.values().length];for(int i=0;i<herbsByType.length;i++){herbsByType[i] = new HashSet<Herb>();}for(Herb h:garden){herbsByType[h.type.ordinal()].add(h);}for(int i=0;i<herbsByType.length;i++){System.out.printf("%s:%s%n", Herb.Type.values()[i],herbsByType[i]);} ??執(zhí)行結(jié)果:
ANNUAL:[f5, f7, f1]
PERENNIAL:[f4, f2, f9]
BIENNTAL:[f8, f3, f6] ??這樣的方法確實(shí)可行。可是影藏著很多問(wèn)題。由于數(shù)組不能和泛型兼容。程序須要進(jìn)行未受檢的轉(zhuǎn)換,而且不能正確無(wú)誤地進(jìn)行編譯。由于數(shù)組不知道它的索引代表著什么,你必須手工標(biāo)注這些索引的輸出。可是這樣的方法最嚴(yán)重的問(wèn)題在于。當(dāng)你訪問(wèn)一個(gè)依照枚舉的敘述進(jìn)行索引的數(shù)組時(shí),使用正確的int值就是你的職責(zé)了。int不能提供枚舉的類型安全。
??可是你能夠用EnumMap改善這個(gè)程序:
Herb[] garden = new Herb[]{new Herb("f1",Herb.Type.ANNUAL),new Herb("f2",Herb.Type.PERENNIAL),new Herb("f3",Herb.Type.BIENNTAL),new Herb("f4",Herb.Type.PERENNIAL),new Herb("f5",Herb.Type.ANNUAL),new Herb("f6",Herb.Type.BIENNTAL),new Herb("f7",Herb.Type.ANNUAL),new Herb("f8",Herb.Type.BIENNTAL),new Herb("f9",Herb.Type.PERENNIAL)};Map<Herb.Type, Set<Herb>> herbsByType = new EnumMap<>(Herb.Type.class);for(Herb.Type t : Herb.Type.values()){herbsByType.put(t, new HashSet<Herb>());}for(Herb h:garden){herbsByType.get(h.type).add(h);}System.out.println(herbsByType); ??執(zhí)行結(jié)果:
{ANNUAL=[f7, f1, f5], PERENNIAL=[f4, f2, f9], BIENNTAL=[f8, f6, f3]} ??這段程序更剪短、更清楚,也更安全。執(zhí)行速度方面能夠與使用序數(shù)的數(shù)組相媲美。注意EnumMap構(gòu)造器採(cǎi)用鍵類型的Class對(duì)象:這是一個(gè)有限制的類型令牌,它提供了執(zhí)行時(shí)的泛型信息。
總結(jié)
??EnumMap是專門(mén)為枚舉類型量身定做的Map實(shí)現(xiàn)。
盡管使用其他的Map實(shí)現(xiàn)(如HashMap)也能完畢枚舉類型實(shí)例到值得映射,可是使用EnumMap會(huì)更加高效:它僅僅能接收同一枚舉類型的實(shí)例作為鍵值。而且由于枚舉類型實(shí)例的數(shù)量相對(duì)固定而且有限,所以EnumMap使用數(shù)組來(lái)存放與枚舉類型相應(yīng)的值。這使得EnumMap的效率很高。EnumMap在內(nèi)部使用枚舉類型的ordinal()得到當(dāng)前實(shí)例的聲明次序,并使用這個(gè)次序維護(hù)枚舉類型實(shí)例相應(yīng)值在數(shù)組的位置。
參考資料:
1. 《Java枚舉類型enum》
2. 《Effective Java(Second Edition)》. Joshua Bloch.
3. 《EnumMap與Enumset的使用 》
轉(zhuǎn)載于:https://www.cnblogs.com/yangykaifa/p/7388563.html
總結(jié)
以上是生活随笔為你收集整理的Java集合框架:EnumMap的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Luogu P1087 FBI树
- 下一篇: 齐博cms 7.0 漏洞分析