JAva-EnumMap、IdentityHashMap、WeakHashMapソースコード分析
56502 ワード
public EnumMap(Class keyType) {
this.keyType = keyType;
//
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
}
public EnumMap(EnumMap m) {
keyType = m.keyType;
keyUniverse = m.keyUniverse;
vals = m.vals.clone();
size = m.size;
}
public EnumMap(Map m) {
// m EnumMap ,
if (m instanceof EnumMap) {
EnumMap em = (EnumMap) m;
keyType = em.keyType;
keyUniverse = em.keyUniverse;
vals = em.vals.clone();
size = em.size;
} else {
// EnumMap , vals
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);
}
}
上にはEnumMapの3つのコンストラクタがあり、このクラスに格納されているkey-valueペアは、key値タイプが列挙タイプである必要があることをコンストラクタが知っています.列挙タイプの値はこのメソッドgetKeyUniverse(keyType)によって列挙クラス配列を取得し、keyUniverse配列はkey集合を格納する.vals配列はvalueデータを格納します.2、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);
}
private void typeCheck(K key) {
Class keyClass = key.getClass();
if (keyClass != keyType && keyClass.getSuperclass() != keyType)
throw new ClassCastException(keyClass + " != " + keyType);
}
putメソッドはkey-valueペアに格納されます.key値は固定列挙クラスの定数値であり、コンストラクタの初期化中に取得されたため、ここでputメソッドでは、vals配列にvalue値を保存するためにkey値がここに入力される.まず、keyタイプがコンストラクタの初期化中にバインドされた列挙タイプであるかどうかを確認し、そうでない場合は例外ClassCastExceptionを放出します.次にkey値を計算し、valueがvals配列に格納されたインデックス値を計算し、value値を格納します.元にvalue値がある場合は、元のvalue値を置き換えます.このことからvals長は列挙クラス定数値の個数に過ぎないことが分かる.key値は列挙クラス定数値のみです.put法によってEnumMap類の特徴を基本的に理解し,他の方法はこの考えに従って行われている.
public void putAll(Map extends K, ? extends V> m) {
if (m instanceof EnumMap) {
EnumMap extends K, ? extends V> em =
(EnumMap extends K, ? extends V>)m;
if (em.keyType != keyType) {
if (em.isEmpty())
return;
throw new ClassCastException(em.keyType + " != " + keyType);
}
for (int i = 0; i < keyUniverse.length; i++) {
Object emValue = em.vals[i];
if (emValue != null) {
if (vals[i] == null)
size++;
vals[i] = emValue;
}
}
} else {
/**
* putAll(m)
* , put
* 。
* put(e.getKey(), e.getValue());
* put , put
* put key , ,
*
*/
super.putAll(m);
}
}
keyUniverseは,初期化時に保存された列挙クラス定数値配列である.以上の方法で,集合中のデータを非常に簡単に保存する.さらにEnumMapは配列保存key-valueペアを採用し、管理が便利で、論理が複雑ではありません.
3、getメソッド
public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum)key).ordinal()]) : null);
}
/**
* Returns true if key is of the proper type to be a key in this
* enum map.
* key
* key 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;
}
getメソッドは簡単で,keyが伝達する合理的なパラメータさえあれば,すぐに結果が得られる.O(1)の時間的複雑さ.上記のputメソッドとgetメソッドを組み合わせると,EnumMapクラスがkey-value保存を行う場合,keyは1つの配列で保存され,vaule値は配列valsの対応する位置に格納される.指定したkeyのvalueを取得する必要がある場合、keyを直接介して配列valsの対応する位置の値を得ることができます.競合する問題は発生しません.Enumクラスごとに列挙値が異なるためです.
4、削除方法
public V remove(Object key) {
if (!isValidKey(key))
return null;
int index = ((Enum)key).ordinal();
Object oldValue = vals[index];
vals[index] = null;
if (oldValue != null)
size--;
return unmaskNull(oldValue);
}
private boolean removeMapping(Object key, Object value) {
if (!isValidKey(key))
return false;
int index = ((Enum)key).ordinal();
if (maskNull(value).equals(vals[index])) {
vals[index] = null;
size--;
return true;
}
return false;
}
/**
* Returns true if key is of the proper type to be a key in this
* enum map.
* key
* key 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;
}
削除方法は簡単です.key値は削除されません.key値は列挙クラス定数値なので、vals配列の対応する位置のvalue値だけが削除されます.remove(key)は、key値に対応する位置のvals配列の値を削除する.removeMapping(key,value)は、対応する位置にあるvalusに等しいvals配列のデータを削除するものである.待たない場合は削除しません.
5指定されたデータメソッドがあるかどうか
/**
* @param key the key whose presence in this map is to be tested
* @return true if this map contains a mapping for the specified
* key
* key value null
* ,put key-value ,
* value null, maskNull() null , NULL
* vals value null 。
*/
public boolean containsKey(Object key) {
return isValidKey(key) && vals[((Enum)key).ordinal()] != null;
}
private boolean containsMapping(Object key, Object value) {
return isValidKey(key) &&
maskNull(value).equals(vals[((Enum)key).ordinal()]);
}
上の2つの方法がわかると、EnumMapクラスがkey-valueペアの管理をどのように行っているのかがわかりやすくなります.key値は初期化時に取得され、配列に保存され、value値はputまたはputAllメソッドで配列に追加され、containsKey(Object key)メソッドはkey値配列ではなく、key値対応位置のvalue値が進行しているか否かで判断される.すなわちkey値に対応するvals配列に値があるかどうかは,EnumMapクラスオブジェクトにkey値が含まれているかどうかを決定する.EnumMapクラスの他の方法は何も言うことはありません.みんな見てください.HashMapクラスが分かれば、他のクラスは何も言いません.
/**
* Constructs a new, empty identity hash map with a default expected
* maximum size (21).
*
*/
public IdentityHashMap() {
init(DEFAULT_CAPACITY);
}
/**
* Constructs a new, empty map with the specified expected maximum size.
* Putting more than the expected number of key-value mappings into
* the map may cause the internal data structure to grow, which may be
* somewhat time-consuming.
*
* @param expectedMaxSize the expected maximum size of the map
* @throws IllegalArgumentException if expectedMaxSize is negative
*
* expectedMaxSize , 。
* expectedMaxSize 。
* capacity(expectedMaxSize) ,
* 。
*/
public IdentityHashMap(int expectedMaxSize) {
if (expectedMaxSize < 0)
throw new IllegalArgumentException("expectedMaxSize is negative: "
+ expectedMaxSize);
init(capacity(expectedMaxSize));
}
/**
* Returns the appropriate capacity for the specified expected maximum
* size. Returns the smallest power of two between MINIMUM_CAPACITY
* and MAXIMUM_CAPACITY, inclusive, that is greater than
* (3 * expectedMaxSize)/2, if such a number exists. Otherwise
* returns MAXIMUM_CAPACITY. If (3 * expectedMaxSize)/2 is negative, it
* is assumed that overflow has occurred, and MAXIMUM_CAPACITY is returned.
* expectedMaxSize 1.5
* 2 。
* result 。
*/
private int capacity(int expectedMaxSize) {
// Compute min capacity for expectedMaxSize given a load factor of 2/3
int minCapacity = (3 * expectedMaxSize)/2;
// Compute the appropriate capacity
int result;
if (minCapacity > MAXIMUM_CAPACITY || minCapacity < 0) {
result = MAXIMUM_CAPACITY;
} else {
result = MINIMUM_CAPACITY;
while (result < minCapacity)
result <<= 1;
}
return result;
}
/**
* Initializes object to be an empty map with the specified initial
* capacity, which is assumed to be a power of two between
* MINIMUM_CAPACITY and MAXIMUM_CAPACITY inclusive.
* initCapacity
* initCapacity !
* IdentityHashMap HashMap 。
* , 。
* 。
*/
private void init(int initCapacity) {
// assert (initCapacity & -initCapacity) == initCapacity; // power of 2
// assert initCapacity >= MINIMUM_CAPACITY;
// assert initCapacity <= MAXIMUM_CAPACITY;
threshold = (initCapacity * 2)/3;
table = new Object[2 * initCapacity];
}
/**
* @param m the map whose mappings are to be placed into this map
* @throws NullPointerException if the specified map is null
*/
public IdentityHashMap(Map extends K, ? extends V> m) {
// Allow for a bit of growth
this((int) ((1 + m.size()) * 1.1));
putAll(m);
}
コンストラクタでは、IdentityHashMapが初期化時に比較的大きなmap配列を構築して、可能な競合問題を解決し、データを配列に格納することを知っています.また,クエリ効率を向上させるために,限界容量設定は比較的小さく,配列長の3分の1しかない.しかし、問題も発生し、メモリ容量が大きすぎます.すなわち,有効利用される空間は配列の全長の3分の1に満たない.
2 putメソッド
/**
* 。 ,IdentityHashMap
* key-value , ,
* nextKeyIndex(i,len) ,
* , , nextKeyIndex(i,len)
* 。
* , key , key,
* value value 。
* put , resize 。
* >= , 。
* put , table ,
* 。
* init() , ,
* ,
* IdentityHashMap 。
*/
public V put(K key, V value) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
Object item;
while ( (item = tab[i]) != null) {
if (item == k) {
V oldValue = (V) tab[i + 1];
tab[i + 1] = value;
return oldValue;
}
i = nextKeyIndex(i, len);
}
modCount++;
tab[i] = k;
tab[i + 1] = value;
if (++size >= threshold)
resize(len); // len == 2 * current capacity.
return null;
}
/**
* @param newCapacity the new capacity, must be a power of two.
*
* 。
* , 。
* , null.
* : , ,
* !
* ,
* 。
* , , 。
*/
private void resize(int newCapacity) {
// assert (newCapacity & -newCapacity) == newCapacity; // power of 2
int newLength = newCapacity * 2;
Object[] oldTable = table;
int oldLength = oldTable.length;
if (oldLength == 2*MAXIMUM_CAPACITY) { // can't expand any further
if (threshold == MAXIMUM_CAPACITY-1)
throw new IllegalStateException("Capacity exhausted.");
threshold = MAXIMUM_CAPACITY-1; // Gigantic map!
return;
}
if (oldLength >= newLength)
return;
Object[] newTable = new Object[newLength];
threshold = newLength / 3;
for (int j = 0; j < oldLength; j += 2) {
Object key = oldTable[j];
if (key != null) {
Object value = oldTable[j+1];
oldTable[j] = null;
oldTable[j+1] = null;
int i = hash(key, newLength);
while (newTable[i] != null)
i = nextKeyIndex(i, newLength);
newTable[i] = key;
newTable[i + 1] = value;
}
}
table = newTable;
}
/**
* @param m mappings to be stored in this map
* @throws NullPointerException if the specified map is null
* putAll , put 。
* 。 。
*/
public void putAll(Map extends K, ? extends V> m) {
int n = m.size();
if (n == 0)
return;
if (n > threshold) // conservatively pre-expand
resize(capacity(n));
for (Entry extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}
/**
* Circularly traverses table of size len.
* key
*/
private static int nextKeyIndex(int i, int len) {
return (i + 2 < len ? i + 2 : 0);
}
以上の方法により,このクラスはkey−valueペアの格納を完了すると,key−valueペアを配列に隣接して格納することが分かった.ステップ2を間隔としてデータの埋め込みを行います.
3、クエリーによるデータ取得方法
/**
* key value 。
* ,item==k,
* , value null。
* ,item==k ,tab[i+1] value 。
* i key ,i+1 value 。
* 2 。
* @see #put(Object, Object)
*/
public V get(Object key) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k)
return (V) tab[i + 1];
if (item == null)
return null;
i = nextKeyIndex(i, len);
}
}
/**
* @param key possible key
* @return true
if the specified object reference is a key
* in this map
* @see #containsValue(Object)
* key 。 get(key)
* 。
*/
public boolean containsKey(Object key) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k)
return true;
if (item == null)
return false;
i = nextKeyIndex(i, len);
}
}
/**
* @param value value whose presence in this map is to be tested
* @return true if this map maps one or more keys to the
* specified object reference
* @see #containsKey(Object)
* value 。
* i 2
* value 。
* IdentityHashMap iden =
new IdentityHashMap<>();
iden.put(null, null);
iden.put(null, null);
System.out.println(iden);
System.out.println(iden.containsKey(null));
System.out.println(iden.containsValue(null));
:
{null=null}
true
true
* key value 。
* key==null , maskKey(key)
* key=null , NULL_KEY 。
* put 。
*/
public boolean containsValue(Object value) {
Object[] tab = table;
for (int i = 1; i < tab.length; i += 2)
if (tab[i] == value && tab[i - 1] != null)
return true;
return false;
}
/**
* @param key possible key
* @param value possible value
* @return true
if and only if the specified key-value
* mapping is in the map
* key-value 。
* containsKey containsValue
*/
private boolean containsMapping(Object key, Object value) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k)
return tab[i + 1] == value;
if (item == null)
return false;
i = nextKeyIndex(i, len);
}
}
上の3つはmap配列中のデータをクエリーする方法で、上の方法はkeyのmap配列中のインデックスを取得することによってクエリーを行い、クエリーが結果まで分かる.
4、データの削除
/**
* key 。
* key , hash , 。
* key , 。 size-1.
* closeDeletion(i) remap
* 。
*/
public V remove(Object key) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k) {
modCount++;
size--;
V oldValue = (V) tab[i + 1];
tab[i + 1] = null;
tab[i] = null;
closeDeletion(i);
return oldValue;
}
if (item == null)
return null;
i = nextKeyIndex(i, len);
}
}
/**
* key-value 。
* remove 。
* key hash , key
* key , key == ,
* 。
* , closeDeletion(i) rehash
* 。
* == key , false, 。
*/
private boolean removeMapping(Object key, Object value) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k) {
if (tab[i + 1] != value)
return false;
modCount++;
size--;
tab[i] = null;
tab[i + 1] = null;
closeDeletion(i);
return true;
}
if (item == null)
return false;
i = nextKeyIndex(i, len);
}
}
/**
* rehash
* 。
* if 。
* hash 。
* 。
* , 。
* 。
*/
private void closeDeletion(int d) {
Object[] tab = table;
int len = tab.length;
Object item;
for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;
i = nextKeyIndex(i, len) ) {
int r = hash(item, len);
if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) {
tab[d] = item;
tab[d + 1] = tab[i + 1];
tab[i] = null;
tab[i + 1] = null;
d = i;
}
}
}
IdentityHashMapは、key-valueデータを配列に保存し、長い配列で競合を解決するため、データの削除が便利ですが、key値に対応するvalueは、全等の場合にのみ削除されます.また、一対のデータを削除した後、後続のデータが遍歴できないため、一対のデータを削除した後、後続のデータをmap配列上に書き換える必要がある.
5等しい方法とhashcode方法
/**
* IdentityHashMap
* equals Map , entrySet().equals(m.entrySet())
* true 。
* 1、 o IdentityHashMap , containsMapping
* key-value 。
* 2、 o containsMapping , entrySet()
* EntrySet 。
* EntrySet , equals , AbstractSet
* equals , equals , ,
* AbstractSet AbstractCollection
* containsAll(Collection> c) 。
* o (o.equals(it.next()))
* 。
* , o IdentityHashMap , o equals
* , == 。
*/
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof IdentityHashMap) {
IdentityHashMap m = (IdentityHashMap) o;
if (m.size() != size)
return false;
Object[] tab = m.table;
for (int i = 0; i < tab.length; i+=2) {
Object k = tab[i];
if (k != null && !containsMapping(k, tab[i + 1]))
return false;
}
return true;
} else if (o instanceof Map) {
Map m = (Map)o;
return entrySet().equals(m.entrySet());
} else {
return false; // o is not a Map
}
}
/**
* hash 。
* hash key value
* key-value hash 。
*/
public int hashCode() {
int result = 0;
Object[] tab = table;
for (int i = 0; i < tab.length; i +=2) {
Object key = tab[i];
if (key != null) {
Object k = unmaskNull(key);
result += System.identityHashCode(k) ^
System.identityHashCode(tab[i + 1]);
}
}
return result;
}
この2つの方法はクラスの鍵であり、hashcodeの計算はkey値だけでなくvalue値にも関係しており、key-value対が一意のhash値を持つことを保証します.同時にequalsメソッドを書き換えることにより,key値が全等の場合にのみkey値が等しいと判定する.これがIdentityHashMapが通常のHashMapとは異なるポイントです.
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
int hash;
Entry next;
/**
* Creates new entry.
*/
Entry(Object key, V value,
ReferenceQueue
これはWeakHashMapクラスの内部クラスEntryの定義であり、これは通常のHashMapとは異なる鍵である.Entryコンストラクタでは、各Entryを参照キューに関連付け、各Entryオブジェクトは弱い参照です!!!この弱い参照はkey値を指します.2.WeakHashMapコンストラクタ
/**
* HashMap
*/
public WeakHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Initial Capacity: "+
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal Load factor: "+
loadFactor);
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
table = newTable(capacity);
this.loadFactor = loadFactor;
threshold = (int)(capacity * loadFactor);
useAltHashing = sun.misc.VM.isBooted() &&
(capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
}
public WeakHashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public WeakHashMap() {
this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}
public WeakHashMap(Map extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY),
DEFAULT_LOAD_FACTOR);
putAll(m);
}
HashMapと比較すると,両者のコンストラクタには何の違いもないことが分かったが,これはWeakHashMapが通常のHashMapと記憶データ上同じであることを示していると言える.
3 putメソッド
public V put(K key, V value) {
Object k = maskNull(key);
int h = hash(k);
Entry[] tab = getTable();
int i = indexFor(h, tab.length);
for (Entry e = tab[i]; e != null; e = e.next) {
if (h == e.hash && eq(k, e.get())) {
V oldValue = e.value;
if (value != oldValue)
e.value = value;
return oldValue;
}
}
modCount++;
Entry e = tab[i];
tab[i] = new Entry<>(k, value, queue, h, e);
if (++size >= threshold)
resize(tab.length * 2);
return null;
}
/**
* Expunges stale entries from the table.
* WeakHashMap
* getSize() getTable()
* :
*
* map ,map 。
* queue.poll() , Entry WeakReference
* : ,
* map , map
* map 。 map
*/
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry e = (Entry) x;
int i = indexFor(e.hash, table.length);
Entry prev = table[i];
Entry p = prev;
while (p != null) {
Entry next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
/**
* map
* expungeStaleEntries() map
*/
private Entry[] getTable() {
expungeStaleEntries();
return table;
}
putメソッドはデータを格納し、key値がすでに存在する場合は元のvalueを置き換え、存在しない場合は適切な位置に入力します.競合が発生した場合、チェーンテーブルを使用して解決されることがわかります.これは一般的なHashMapと一致する.異なる点は、データを格納する前にgetTable()メソッドを使用してまずmap配列を取得し、expungeStaleEntries()メソッドを呼び出し、上記の注釈部分でできるだけ自分の理解を与えたことです.expungeStaleEntries()メソッドはWeakHashMapの正常な動作を保証する重要なメソッドと言える.
4、データ取得方法
/**
* @see #put(Object, Object)
* getEntry(Object key)
* value ??
*/
public V get(Object key) {
Object k = maskNull(key);
int h = hash(k);
Entry[] tab = getTable();
int index = indexFor(h, tab.length);
Entry e = tab[index];
while (e != null) {
if (e.hash == h && eq(k, e.get()))
return e.value;
e = e.next;
}
return null;
}
public boolean containsKey(Object key) {
return getEntry(key) != null;
}
Entry getEntry(Object key) {
Object k = maskNull(key);
int h = hash(k);
Entry[] tab = getTable();
int index = indexFor(h, tab.length);
Entry e = tab[index];
while (e != null && !(e.hash == h && eq(k, e.get())))
e = e.next;
return e;
}
上記はデータを取得する方法であり,通常のHashMapとは大きく異なり,主にデータ取得を行うたびにmap配列を取得する.
5、データの削除
public V remove(Object key) {
Object k = maskNull(key);
int h = hash(k);
Entry[] tab = getTable();
int i = indexFor(h, tab.length);
Entry prev = tab[i];
Entry e = prev;
while (e != null) {
Entry next = e.next;
if (h == e.hash && eq(k, e.get())) {
modCount++;
size--;
if (prev == e)
tab[i] = next;
else
prev.next = next;
return e.value;
}
prev = e;
e = next;
}
return null;
}
boolean removeMapping(Object o) {
if (!(o instanceof Map.Entry))
return false;
Entry[] tab = getTable();
Map.Entry,?> entry = (Map.Entry,?>)o;
Object k = maskNull(entry.getKey());
int h = hash(k);
int i = indexFor(h, tab.length);
Entry prev = tab[i];
Entry e = prev;
while (e != null) {
Entry next = e.next;
if (h == e.hash && e.equals(entry)) {
modCount++;
size--;
if (prev == e)
tab[i] = next;
else
prev.next = next;
return true;
}
prev = e;
e = next;
}
return false;
}
削除も比较的简単で、普通のHashMapと特别なところはありません.
まとめてみます:上の3つのクラスの中の反復器は私はすべて与えていません.前の2つの博文のソースコードのようにすべて与えていません.HashMapクラスのソースコードの部分を理解すれば、他のクラスは触類バイパスと言えます.何も言わないで!重要な違いは各クラスの特徴にある.
1、EnumMapクラスは列挙クラスに関連付ける必要があります.key値は列挙クラスの定数値のみです.valueは配列格納を用い,長さは列挙クラスの定数値の個数である.2、IdentityHashMapクラスは、全等判定key値、不全等のkey値を用いて、2つのオブジェクトとしてmap配列に入れることができる.さらにIdentityHashMapはチェーンテーブルで競合を解決するのではなく、大きなmap配列を使用してすべてのkey-valueペアを格納します.同時にkey−value対はmap配列の奇数ビットと偶数ビットに隣接して格納される.このため、IdentityHashは大きなメモリ容量を消費します.実際に使用されるmap配列の有限長さは、総長の3分の1を占めることが多い.3.WeakHashMapクラスはリファレンスキューを使用して回収した弱いリファレンスを格納する.主にメモリ容量が不足している場合に使用されます.このような使用には注意が必要です.いつ、map配列のデータが回収されるか分からないからです.クラスの定義は次のとおりです.
public class WeakHashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>
これは、クラスがシーケンス化、逆シーケンス化、クローン化できないことを示しています!!!弱いリファレンスが格納されている以上、クローン化されている場合は、深いコピーでなければなりません.シーケンス化する場合は、map配列が空で必要ない場合があります.また、WeakHashMapが使用されている以上、メモリ領域が緊張していることを示し、クローン化やシーケンス化の逆シーケンス化を行う必要はありません.ただし、このクラスのentrySet()メソッドで返されるSet
public Set> entrySet() {
Set> es = entrySet;
return es != null ? es : (entrySet = new EntrySet());
}
private class EntrySet extends AbstractSet> {
public Iterator> iterator() {
return new EntryIterator();
}
public boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry,?> e = (Map.Entry,?>)o;
Entry candidate = getEntry(e.getKey());
return candidate != null && candidate.equals(e);
}
public boolean remove(Object o) {
return removeMapping(o);
}
public int size() {
return WeakHashMap.this.size();
}
public void clear() {
WeakHashMap.this.clear();
}
/**
*
*
* ,
*
*/
private List> deepCopy() {
List> list = new ArrayList<>(size());
for (Map.Entry e : this)
list.add(new AbstractMap.SimpleEntry<>(e));
return list;
}
public Object[] toArray() {
return deepCopy().toArray();
}
public T[] toArray(T[] a) {
return deepCopy().toArray(a);
}
}
この中のtoArray()メソッドは,map配列の値を深コピーして取得することである.考えてみれば正しいですが、浅いコピーなら、回収されているかもしれません.値の取得はあまり役に立たない.クローン化とシーケンス化メカニズムが実現されない理由も基本的に説明した.
以上が個人的な見解です.何か間違いや質問があれば、コメントを歓迎します!たくさん交流します~!共に进歩!!^^;【握手~】