Javaエニュメレーションタイプ(enum)-5
8390 ワード
EnumMap
EumMapの基本的な使い方
まずこのような問題を考えてみましょう。今は同じサイズで、色が違うデータがあります。各色の数を統計して、データを倉庫に入力する必要があります。次のように列挙して、色のColorを表します。
EumMapは原理分析を実現します。
EnumMapのソースコードは700行以上あります。ここでは主に内部の記憶構造を分析し、検索の実現を追加します。この点を理解して、EnumMap内部の実現原理に対応しています。まずデータ構造と構造関数を見ます。
次の章:Javaエニュメレーションタイプ(enum)-6
EumMapの基本的な使い方
まずこのような問題を考えてみましょう。今は同じサイズで、色が違うデータがあります。各色の数を統計して、データを倉庫に入力する必要があります。次のように列挙して、色のColorを表します。
enum Color {
GREEN,RED,BLUE,YELLOW
}
次のような解決策があります。Map集合を使って統計します。key値は色の名前として、valueは服の数を表しています。import java.util.*;
public class EnumMapDemo {
public static void main(String[] args){
List list = new ArrayList<>();
list.add(new Clothes("C001",Color.BLUE));
list.add(new Clothes("C002",Color.YELLOW));
list.add(new Clothes("C003",Color.RED));
list.add(new Clothes("C004",Color.GREEN));
list.add(new Clothes("C005",Color.BLUE));
list.add(new Clothes("C006",Color.BLUE));
list.add(new Clothes("C007",Color.RED));
list.add(new Clothes("C008",Color.YELLOW));
list.add(new Clothes("C009",Color.YELLOW));
list.add(new Clothes("C010",Color.GREEN));
// 1: HashMap
Map map = new HashMap<>();
for (Clothes clothes:list){
String colorName=clothes.getColor().name();
Integer count = map.get(colorName);
if(count!=null){
map.put(colorName,count+1);
}else {
map.put(colorName,1);
}
}
System.out.println(map.toString());
System.out.println("---------------");
// 2: EnumMap
Map enumMap=new EnumMap<>(Color.class);
for (Clothes clothes:list){
Color color=clothes.getColor();
Integer count = enumMap.get(color);
if(count!=null){
enumMap.put(color,count+1);
}else {
enumMap.put(color,1);
}
}
System.out.println(enumMap.toString());
}
/**
:
{RED=2, BLUE=3, YELLOW=3, GREEN=2}
---------------
{GREEN=2, RED=2, BLUE=3, YELLOW=3}
*/
}
コードは簡単です。二つの解決案を使っています。一つはHashMapで、一つはEnumMapです。全部正しい結果を集計しましたが、EnumMapは列挙の専用の集合として、私達はHashMapを使う理由がありません。結局、EnumMapはEnumタイプでなければならないので、Color列挙の実例を使ってkeyとして最適です。nameを取得するステップも避けました。さらに重要なのはEnumMapの効率が高いことです。内部は配列によって実現されるので(後で分析します)、EnumMapのkey値はnullではないことに注意してください。エニュメレーション専用集合ですが、その操作は普通のMapと同じです。概括的にはEnumMapはエニュメレート・タイプのためにカスタマイズされたMapです。他のMapを使用しても同じ機能を達成できますが、EnumMapを使用するとより効率的になります。同じ列挙タイプのインスタンスをキーとして受信することができます。nullは列挙タイプのインスタンスの数が相対的に固定され、限定されていますので、EnumMapは列挙タイプに対応する値を配列で保存します。配列は一連のメモリ空間です。プログラムの局部原理によって、効率がかなり高くなります。EumMapの使い方を詳しく調べて、まず構造関数を見ます。// 。
EnumMap(Class keyType)
// , ( )。
EnumMap(EnumMap m)
// , 。
EnumMap(Map m)
HashMapとは違って、Classオブジェクトのタイプ情報を伝達する必要があります。このパラメータでEnumMapはタイプ情報に基づいて内部データ構造を初期化できます。他の2つは初期化時にMapセットに入ってきます。コードのデモは以下の通りです。//
Map enumMap=new EnumMap<>(Color.class);
//
Map enumMap2=new EnumMap<>(enumMap);
//
Map hashMap = new HashMap<>();
hashMap.put(Color.GREEN, 2);
hashMap.put(Color.BLUE, 3);
Map enumMap = new EnumMap<>(hashMap);
EnumMapの方法は普通のmapとほとんど変わりません。HashMapの主な違いは構造方法と伝達型パラメータとEnumMap保証Key順序と列挙中の順序が一致していることですが、Keyはnullではないことを覚えてください。EumMapは原理分析を実現します。
EnumMapのソースコードは700行以上あります。ここでは主に内部の記憶構造を分析し、検索の実現を追加します。この点を理解して、EnumMap内部の実現原理に対応しています。まずデータ構造と構造関数を見ます。
public class EnumMap, V> extends AbstractMap
implements java.io.Serializable, Cloneable
{
//Class
private final Class keyType;
// Key
private transient K[] keyUniverse;
// Value
private transient Object[] vals;
//map size
private transient int size = 0;
// map
private static final Enum>[] ZERO_LENGTH_ENUM_ARRAY = new Enum>[0];
//
public EnumMap(Class keyType) {
this.keyType = keyType;
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
}
}
EnumMapはAbstractMapクラスを継承していますので、EnumMapは一般的なmapの使用方法を備えています。keyTypeはタイプ情報を表しています。keyUniverseはキー配列を表しています。記憶されているのはすべての可能なエニュメレート・値です。vals配列はキーの対応する値を表しています。構造関数においては、keyUniverse = getKeyUniverse(keyType);
によってkeyUniverse配列の値を初期化し、内部に記憶されているのはすべての可能なエニュメレーション値であり、次いでValue配列の存在価値valsを初期化し、そのサイズはエニュメレート・インスタンスの個数と同じであり、getKeyUniverse方法は以下のように実現される。//
private static > K[] getKeyUniverse(Class keyType) {
// values ,values
return SharedSecrets.getJavaLangAccess()
.getEnumConstantsShared(keyType);
}
方法の戻り値から見ると、戻りのタイプはエニュメレート・アレイであり、事実も同様であり、最終的な戻り値はエニュメレート・タイプのvalues方法の戻り値であり、以前はvalues方法を分析して、可能なエニュメレート・値をすべて返したので、keyUniverse配列記憶はエニュメレート・タイプの可能なエニュメレート・値である。続いてput方法の実現を見ます。public V put(K key, V value) {
typeCheck(key);// key
// value
int index = key.ordinal();
//
Object oldValue = vals[index];
// value
vals[index] = maskNull(value);
if (oldValue == null)
size++;
return unmaskNull(oldValue);//
}
ここではtype Check方法でkeyタイプの検出を行い、列挙タイプかどうかを判断します。タイプが違うと、例外を投げます。private void typeCheck(K key) {
Class> keyClass = key.getClass();//
if (keyClass != keyType && keyClass.getSuperclass() != keyType)
throw new ClassCastException(keyClass + " != " + keyType);
}
次に、int index = key.ordinal()
によって、エニュメレート・インスタンスの順序値を取得し、この値を以下のものとして利用して、vals配列に対応する下付き要素、すなわちvals[index]
に値を格納し、これもエニュメレート・インスタンスと同じ記憶順序を維持できる理由である。vals[]における要素の割り当てと古い値に戻る時にそれぞれmasNull方法とunmaskNull方法を呼び出したことを発見しました。// NULL
private static final Object NULL = new Object() {
public int hashCode() {
return 0;
}
public String toString() {
return "java.util.EnumMap.NULL";
}
};
private Object maskNull(Object value) {
// , NULL , value
return (value == null ? NULL : value);
}
@SuppressWarnings("unchecked")
private V unmaskNull(Object value) {
// NULL null
return (V)(value == NULL ? null : value);
}
このようにEnumMapはやはりnull値を許容していますが、keyは絶対nullではなく、null値に対してEnumMapは特殊処理を行い、NULLオブジェクトに包装しました。結局vals[]はObject、masNull方法とunmaskNull方法はnull包装と和解包装に用いられます。これがEumMap集合の追加過程です。次に取得方法を見ます。public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum>)key).ordinal()]) : null);
}
// Key
private boolean isValidKey(Object key) {
if (key == null)
return false;
// Cheaper than instanceof Enum followed by getDeclaringClass
Class> keyClass = key.getClass();
return keyClass == keyType || keyClass.getSuperclass() == keyType;
}
対応するput方法に対して、get方法はかなり簡潔であり、keyが有効であれば、直接ordinal方法でインデックスを取り、値配列valsでインデックスを取得して返します。removeの方法は以下の通りです。public V remove(Object key) {
// key
if (!isValidKey(key))
return null;
//
int index = ((Enum>)key).ordinal();
Object oldValue = vals[index];
// null
vals[index] = null;
if (oldValue != null)
size--;// size
return unmaskNull(oldValue);
}
非常に簡単で、key値が有効で、keyにより下付きインデックス値を取得し、vals[]に対する下付き値をnullに設定し、sizeを1つ減らす。値が含まれているかどうかを確認します。 value
public boolean containsValue(Object value) {
value = maskNull(value);
//
for (Object val : vals)
if (value.equals(val))
return true;
return false;
}
// key
public boolean containsKey(Object key) {
return isValidKey(key) && vals[((Enum>)key).ordinal()] != null;
}
valueが直接的に巡回配列によって実現されると判断し、keyが有効かどうかを判断し、vals[]に該当する値があるかどうかを判断する。これはEnumMapの主な実現原理です。つまり内部には二つの配列があり、長さは同じです。一つは可能なすべてのキー(列挙値)を表し、一つは対応する値を表しています。keynullは許されませんが、valueはnullとなります。キーには対応するインデックスがあります。インデックスに従って直接アクセスして、そのキー配列と値配列を操作します。次の章:Javaエニュメレーションタイプ(enum)-6