Map.Entry 介面
Map.Entry
Map
介面下面的Entry
介面。
該介面,定義一個鍵值對實體介面。Map.entrySet
方法返回的 Set
集合中的實體就是實現這個 它。只有一種方法可以獲得 Map.Entry
物件的引用,那就是通過集合的迭代器。並且 Map.entry
只在迭代期間有效,更加準確的是意思是,如果在獲得迭代器以後,修改了集合,那麼 Map.Entry
的行為是未定義的1。除非呼叫 Map.Entry
的 setValue
設定下修改的值。
API
文件中的這段話,說的讓我有點費解。修改集合以後,Map.Entry
的行為是未定義的,LZ 做了實驗,發現並沒有觸發到什麼非法,未定義的操作。
Map<String, String> stringMap = new HashMap<>(16); stringMap.put("key1", "value1"); stringMap.put("key2", "value2"); stringMap.put("key3", "value3"); stringMap.put("key4", "value4"); stringMap.put("key5", "value5"); stringMap.put("key6", "value6"); Iterator<Map.Entry<String, String>> iterator = stringMap.entrySet().iterator(); Map.Entry<String, String> next = iterator.next(); stringMap.remove(next.getKey()); stringMap.put(next.getKey(),"value7");
就如上面的程式碼所示,在得到 Entry
以後,對集合進行了修改,也沒有觸發什麼非法的狀態,丟擲什麼異常來。這裡的未定義,其實是一個很無懈可擊的答案,既然是未定義的,那麼它們做出的任何行為,都是可以被理解的,所以它沒丟擲什麼異常,那也是對的,丟擲異常也是對的,你不應該單方面的任認為它應該怎樣怎樣,因為它是未定義,不同的實現有不同的反應。
而且這裡的合法與非法,是針對 Entry
的值來說,在你獲取以後,有人又修改了集合的內容,這時候你獲取的 Entry
的內容,也會隨之改變,但是你可能不知道集合被修改過,所以這裡的合法與非法,是 Entry
是否可以再被信任的問題,所以想要修改值的時候,應該用 entry
setValue()
方法,顯示的去改。
K getKey()
返回實體對應的 key
。
可能丟擲的異常 IllegalStateException
,這個異常可以 選擇性 的實現。如果實現了,則異常的丟擲條件:如果對應的 entry
已經被移除了,則丟擲該異常。
比如,HashMap
的 Entry
就沒有實現丟擲該異常:
static class Node<K,V> implements Map.Entry<K,V> {
...
public final K getKey() { return key; }
...
}
而EnumMap
則實現了該異常,並且遵守了異常丟擲條件:
private class Entry implements Map.Entry<K,V> {
...
public K getKey() {
checkIndexForEntryUse();
return keyUniverse[index];
}
...
private void checkIndexForEntryUse() {
if (index < 0)
throw new IllegalStateException("Entry was removed");
}
}
V getValue()
返回 entry
實體對應的 value
。
如果集合中此 entry
的對映關係已經被移除,即使是通過 iterator
的 remove
方法,getValue()
方法的返回值也是 未定義。因此,不同的實現,對此方法有不同的做法,HashMap
對其沒做什麼,正常返回值,即使對映關係被刪除了。EnumMap
則丟擲異常。
可能丟擲的異常 IllegalStateException
,這個異常可以 選擇性 的實現。如果實現了,則異常的丟擲條件:如果對應的 entry
已經被移除了,則丟擲該異常。
V setValue(V value)
替換當前 entry
的 value
為傳進來的給定的 value
,(map
中對應的 value
也被改變)。如果集合中 entry
的對映關係已經被通過迭代器的 remove()
方法移除,則呼叫這個方法的行為是 未定義 的。看具體的實現如何操作。同樣的 HashMap
對此行為,返回正確的值。EnumMap
則丟擲異常。
返回設定值之前,當前 entry
對應的值。
可能丟擲的異常:
UnsupportedOperationException
:如果集合不支援put
操作,則丟擲此異常。ClassCastException
:如果傳入的引數,不能轉換儲存到集合中,則丟擲此異常,型別轉換異常。NullPointerException
:如果集合不允許存入null
,其傳入的引數確實是null
,則丟擲此異常。IllegalArgumentException
:如果傳入的值的某些屬性,阻止其存入集合中,則丟擲此異常。IllegalStateException
:此異常可選擇是否實現。如果entry
已經被移除了,則丟擲此異常。
boolean equals(Object o)
將傳入的引數物件與當前的 entry
比較,如果傳入的物件也是一個 entry
型別,並且它們具有相同的對映關係,則返回 true
。
更確切的說,相同的對映關係,應該寫成下面的程式碼: key
,value
分別相等。
(e1.getKey()==null ? e2.getKey()==null : e1.getKey().equals(e2.getKey()))
&&
(e1.getValue()==null ? e2.getValue()==null: e1.getValue().equals(e2.getValue()))
這樣做以後,可以確保 equals
方法在不同的 Map.Entry
實現之前都能正確的工作。
int hashCode()
返回當前 entry
的雜湊碼。entry
的雜湊碼計算方法如下:
(e.getKey()==null ? 0 : e.getKey().hashCode())
^
(e.getValue()==null ? 0 : e.getValue().hashCode())
這樣做,確保 e1.equals(e2)
時,e1.hashCode()==e2.hashCode()
,當前前提是,這個兩個 entry
的 KV
的 hashCode
方法一致 。
下面幾個方法是 1.8 新增進來的。屬於靜態方法
comparingByKey()
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
返回一個 Comparator
,該比較器對 entry
的 key
進行 自然排序,即按照字典順序,0-9,a-z 。
返回的比較器,實現了 serializable
介面。程式碼中 (Comparator<Map.Entry<K, V>> & Serializable)
是強轉的含義。強轉可以這樣寫,轉為二者的結合,但是 &
後面必須是 介面 。
可能丟擲的異常:NullPointerException
,如果比較的 entry
的 key
是 null
,則丟擲此異常。
comparingByValue( )
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
}
返回一個 Comparator
,該比較器對 entry
的 key
進行 自然排序 。
返回的比較器,實現了 serializable
介面。
可能丟擲的異常:NullPointerException
,如果比較的 entry
的 key
是 null
,則丟擲此異常。
comparingByKey(Comparator<? super K> cmp)
public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
}
返回一個比較器,該比較器對 entry
的 key
進行比較,根據傳入的比較器。如果傳入的比較器實現了 serializable
介面,那麼返回的比較器也一併實現該介面。
comparingByValue(Comparator<? super V> cmp)
public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
}
返回一個比較器,該比較器對 entry
的 value
進行比較,根據傳入的比較器。如果傳入的比較器實現了 serializable
介面,那麼返回的比較器也一併實現該介面。
可以參考下 codeRanch 上面的回答 。真是令人驚歎,上面關於這個疑問的討論,還是十七年前的回答,當時的他們又是人幾何年呢。如果也如我一樣,那十七年過去了,現在也是不惑之年了。↩