package java.util;
import java.io.Serializable;
import java.lang.*;
import java.util.*;
import java.util.function.*;
import java.util.function.BiFunction;
/**
*
* Map是一個有鍵值對映射的對象.map不能包含相同的key,每一個key至多能映射一個value.
*
* 這個接口替代了Dictionary這個類,Dictionary是抽象類而非接口.
* (替代原因:接口總是優于抽象類,原因可檢視<<effective java>>第18條)
*
* Map接口提供了3個集合視圖,包括:keys的set集合; values的集合; key-value的set集合.(注意:values集合不是set類型,因為value可相同)
* Map傳回元素的順序,取決于:map對應的某個集合視圖疊代器的順序.
* 一些map的實作,比如TreeMap類,對于map傳回元素的順序有特殊的規定;
* 其它的map實作類,比如HashMap類,就沒有特殊的規定.
*
* 注意:如果把易變的對象作為map的key,那麼一定要引起你的特别關注.
* 如果map中的一個key發生了改變,并且影響了equals()方法的使用,那麼map并不會提示我們.
* 是以,我們禁止使用map自身作為map的key.
* 盡管我們允許使用map自身作為value值,但是我們還是要注意:equals()方法和hashCode()方法在這樣定義的map裡面可能不能正常使用.
*
* 所有的通用map實作類都應該提供兩個"标準"的構造器函數:
* 一個是無參且傳回類型為void的函數;
* 另一個就是僅含有一個參數且類型為map類型的的構造方法,這個方法會使用其參數構造一個新的map,且新的map和參數map有相同的key和value.
* 結果就是,第二個構造函數允許使用者複制任何map,新生成一個和給定map一樣的map.
* 但是,map接口這裡沒辦法強制執行這一建議(因為接口裡面不能包含構造器函數),不過JDK裡面所有通用的map實作類都是符合這一點要求的.
*
* 如果一個操作不被map支援,且這個操作會更改map的結構,這樣的操作已經在map接口中做了具體的定義,并且會傳回
* UnsupportedOperationException異常.
* 如果是上述這種情況,這些方法可能會抛出UnsupportedOperationException異常,但并不是說一定會抛出這個異常.而且調用也不會對map産生什麼
* 影響.比如,在一個不可更改的map上面調用不被允許的putAll方法,則會抛出異常.
*
* 一些map的實作類在key和value的取值上面會有一些規定.比如,一些map實作類不允許key或者value為null;
* 而一些是在key的類型上面做了限制.嘗試插入一些不符合規定的key或者value,會抛出非檢查型異常,
* 類似NullPointerException異常,ClassCastException異常.
* 嘗試查詢不符合規定的key或者value也會抛出異常,或者僅傳回false;具體是上述哪種情況,和具體的map實作類自身相關.
* 更一般的情況是,對于非法的key或者value進行的操作,插入失敗可能會抛出異常,但是也可能插入成功,這取決于具體的map實作類.
* 這類型的異常被認為是map接口的可選擇的.
*
* map接口是java集合架構中的一個成員.
*
* 集合架構接口定義了很多種和equals()方法相關的實作.
* 比如,containsKey()方法:目前僅當map包含了鍵k的定義是:key==null ? k==null : key.equals(k)
* 這一規範的寫法,不能被了解為為:如果調用方法使用的是一個非null參數的話,然後隻是再調用key.equals(k)方法就可以了.
* 具體實作可以通過避免調用equals()方法來實作優化,比如,可以先比較兩個key的哈希值.
* (哈希值保證了,如果兩個對象的哈希值都不相同,那麼這兩個對象肯定不會相同).更一般的情況是,大量集合架構接口的實作類可以充分利用
* 底層對象(Object)的方法的優勢,隻要實作者認為他們這麼做是合理的.
*
*
* 注意啦:這裡的containsKey()方法還是和自己想象的不一樣的,我真的以為如果key不是null,那就是直接調用equals()方法啦呢.這一點可以通過
* containsKey()方法調用的getEntry方法源碼看出,在if的判定條件中,equals是作為最後一個判定條件出現的,也就是說如果if前面的判定
* 條件為true,那麼是不會調用equals()方法的.
*
* @author Josh Bloch
* @see java.util.HashMap
* @see TreeMap
* @see Hashtable
* @see SortedMap
* @see Collection
* @see Set
* @since 1.2
*/
public interface Map<K,V> {
//以下為map的查詢操作
/**
* 傳回map中key-value映射的個數.如果map包含的key-value個數超過了Integer.MAX_VALUE這個數,
* 則傳回Integer.MAX_VALUE.
*/
int size();
/**
* 如果map沒有存儲任何key-value,則傳回true.
*/
boolean isEmpty();
/**
* 如果map存儲了指定的key,則傳回true.更一般的情況是,當且僅當map包含了一個key的映射:
* 映射情況是:key==null ? k==null : key.equals(k),此時傳回true.
*/
boolean containsKey(Object key);
/**
* 如果map中至少有一個key能映射到指定的value,那麼就傳回true.更一般的情況是,當且僅當value==null ? v==null : value.equals(v)
* 條件成立,才傳回true.
* 在所有map接口的實作類中,這一操作都需要map大小的線性時間來完成.
*/
boolean containsValue(Object value);
/**
* 傳回指定key映射的value.如果map沒有指定的key,則傳回null.
*
* 更一般的情況是:如果map包含了一個滿足條件key==null ? k==null :key.equals(k))的映射,
* 那麼方法就會傳回對應的value;否則,傳回null.(當然這樣的映射最多有一個)
*
* 如果map允許null值,傳回null值并不一定意味着map不存在指定key的映射;因為這也可能是這個key對應的value值就是null.
* 是以,get方法也可能被用于區分這兩種情況.
*/
V get(Object key);
// Modification Operations ,以下為修改map的操作
/**
* put方法是将指定的key-value存儲到map裡面的操作.如果map之前包含了一個此key對應的映射,那麼此key對應的舊value值會被
* 新的value值替換.
*/
V put(K key, V value);
/**
* remove方法用于移除map中已有的某個key.更一般的講,如果map包含了一個滿足條件key==null ? k==null : key.equals(k)
* 的映射,這一映射就會被移除.(map最多包含一個這樣的映射)
*
* 本方法會傳回移除的key對應的value值,如果map這個key沒有對應的value值,則傳回null.
*
* 如果map允許null值,那麼傳回null值并不一定表明map不包含對應key的映射值;因為這也可能是key本身對應的value值就是null.
*
* 一旦此方法被調用,那麼map就不會再包含這個key的映射了.
*/
V remove(Object key);
//塊操作
/**
* putAll方法是将一個指定map的映射拷貝到目前map.這一操作類似于将指定map的key-value對通過put方法一個個拷貝過來.
* 在拷貝過程中,如果指定的這個map被更改了,那麼這時候會出現什麼情況,并不清楚.
*/
void putAll(Map<? extends K, ? extends V> m);
/**
* 移除map中所有的映射.
* 調用此方法後,map會變為空.
*/
void clear();
// Views ,視圖
/**
* 此方法:傳回map包含所有的key的一個set集合視圖.
* 這個set集合由map作為背景支援,是以map的改變會反映在set集合裡面,同樣的
* set集合的更改也會反映在map裡面.(這和ArrayList中的subList方法一樣,任意一方的更改都會在另一方展現出來)
* 如果在疊代過程中,map被修改了結構(出去通過疊代器的remove方法對map結構的改變),疊代器的輸出結果會受到什麼影響,
* 這一點并沒有給出規定.
* 此方法傳回的set集合支援移除元素,同時會從map中删除對應的映射.可以删除元素的操作方法有5個:
* <tt>Iterator.remove</tt>,
* <tt>Set.remove</tt>,
* <tt>removeAll</tt>,
* <tt>retainAll</tt>,
* <tt>clear</tt>
* 此方法傳回的set集合不支援add或者addAll操作.
* 也就是隻有删除操作,沒有添加操作.畢竟add類型的操作是沒有意義的.因為此set集合傳回的是map的keys的集合.你添加了key,
* 但是沒有辦法同時添加對應的value,隻會給後面的操作帶來性能影響,沒有其它什麼意義.
* 如果你執行了add類型操作會抛出UnsupportedOperationException異常.(這裡可以自己運作一下代碼)
*/
Set<K> keySet();
/**
* values方法傳回map記憶體儲的所有值的集合(畢竟值集合中,值可以有重複的,是以此方法和上面的傳回的key集合的結果類型不一樣,因為key肯定
* 都是不同的).
* 這一集合也由map集合提供背景支援.是以map的更改會展現在傳回的集合裡面,反之亦然.(這一點和keySet方法完全一樣)
* 基于這個集合疊代器,對map進行周遊.在周遊過程中,如果map結構發生了改變(改變不包括:疊代器自身執行的remove方法),疊代器的輸出結果是否
* 受到影響,這一點并沒有定義.
* 這一傳回集合支援移除元素,這會同時移除map中對應的映射.移除操作類型包括5類:
* <tt>Iterator.remove</tt>,
* <tt>Collection.remove</tt>,
* <tt>removeAll</tt>,
* <tt>retainAll</tt>,
* <tt>clear</tt>
*
* 注意:傳回集合不支援add或者addAll操作.如果你執行add類型操作會抛出UnsupportedOperationException異常.(這裡可以自己測試一下,
* 能加深了解.)
*
*
* @return a collection view of the values contained in this map
*/
Collection<V> values();
/**
* 此方法傳回map裡存儲的所有映射的視圖.
* 當然,這個傳回的set集合,也由map作為背景支援(這一點和前兩種方法一樣),是以對map的改變會展現在set上面,反之亦然.
* 基于set集合的疊代器在周遊過程中,如果map結構發生了改變(除去疊代器自身的remove方法造成的map結構改變,或者疊代器對map條目調用了setValue
* 方法),則疊代器對輸出結果的影響是怎麼樣的,并沒有給出定義.
* 傳回的set集合支援移除元素,這種移除操作會同時反映在map上面.
* 移除操作類型包括5種:
* <tt>Iterator.remove</tt>,
* <tt>Set.remove</tt>,
* <tt>removeAll</tt>,
* <tt>retainAll</tt>,
* <tt>clear</tt>
*/
Set<Map.Entry<K, V>> entrySet();
/**
* map條目(key-value對).
* Map.entrySet方法傳回的就是map的集合視圖,map視圖中的元素就是來源于此類.
* 擷取map條目的唯一方式就是來源于集合視圖的疊代器.隻有在疊代的過程中,Map.Entry對象才是有效的;
* 通常,如果通過疊代器獲得的map條目,在周遊過程中,作為背景支援的map被修改了,那麼map條目會如何被影響,對此
* 并沒有做出具體規定(當然此處說的map修改不包括setValue方法的調用).
*/
interface Entry<K,V> {
/**
* 擷取目前map條目對應的key
*/
K getKey();
/**
* 傳回map條目對應的value值.
* 如果映射從背景map中移除了(通過疊代器的remove方法),這一調用的結果會出現什麼影響未被定義.
*/
V getValue();
/**
* 用指定值替換目前條目中的value.(這一更改會被寫入map中,自己調試一下代碼就能知道了).
* 如果映射從map中被移除了,再調用這一方法,會産生什麼異常并沒有定義.
*/
V setValue(V value);
/**
* 将指定對象和目前條目做比較.
* 如果給定的對象是一個條目并且兩個條目代表同一個映射,則傳回true.
* 一般,兩個條目擁有相同映射滿足的條件是:
* if(
* (e1.getKey()==null ?
* e2.getKey()==null : e1.getKey().equals(e2.getKey())) &&
* (e1.getValue()==null ?
* e2.getValue()==null : e1.getValue().equals(e2.getValue()))
* )
* 上面的兩個條件保證了,對Map.Entry接口的不同實作,equals方法都能正确.
*/
boolean equals(Object o);
/**
* 傳回map條目的哈希值.
* map條目的哈希值定義是:二者求異或值
* (e.getKey()==null ? 0 : e.getKey().hashCode()) ^
* (e.getValue()==null ? 0 : e.getValue().hashCode())
* 這才能保證兩個條目相等時,通過Object.hashCode方法得到的他們的哈希值也一定是相等的.
*/
int hashCode();
}
// 比較和hash操作
/**
* 用于對比兩個map是否相等.
* 如果給定的對象是一個map且兩個map的映射一緻,則傳回true.
* 一般,兩個map的映射一緻,要滿足的條件是:
* m1.entrySet().equals(m2.entrySet())
* 這就保證了實作了map接口的不同類對于equals方法的使用才是正确的.
*/
boolean equals(Object o);
/**
* 傳回map的哈希值.
* map的哈希值被定義為:這個map的entrySet視圖的每一個條目的哈希值的總和.
* 這就保證了任意兩個map相等,則他們的哈希值一定相等,這也是Object類對哈希值的普遍要求(哈希值作為兩個對象相等的
* 必要非充分條件).
* @see Map.Entry#hashCode()
* @see Object#equals(Object)
* @see #equals(Object)
*/
int hashCode();
/*----------以下均為jdk8新增内容-----------*/
/**
* 傳回一個比較map.entry的比較器,按照key的自然順序排序.
* 傳回的比較器支援序列化.
* 如果map中的entry有key=null情況,則抛出空指針異常(因為傳回結果要按照key排序)
* 注意:傳入參數k必須支援Comparable接口,因為需要按照key排序.
* @see java.lang.Comparable
* @since 1.8
*/
public static <K extends java.lang.Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
/**
* 傳回一個map.entry的比較器,按照value的自然順序排序.
* 傳回的比較器支援序列化.
* 如果map中的entry有value=null情況,則抛出空指針異常(因為傳回結果要按照value排序)
* 注意:傳入參數value必須支援Comparable接口,因為按照value排序.
* @see java.lang.Comparable
* @since 1.8
*/
public static <K, V extends java.lang.Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
}
/**
* 傳回一個map.entry的比較器,根據傳入比較器對key排序.
* 如果傳入的比較器支援序列化,則傳回的結果比較器也支援序列化.
* @since 1.8
*/
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());
}
/**
* 傳回一個map.entry的比較器,根據傳入比較器對value排序.
* 如果傳入的比較器支援序列化,則傳回的結果比較器也支援序列化.
* @since 1.8
*/
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());
}
}
/*----以下為:比較方法 and hash方法-----*/
/**
* 将傳入對象和目前map進行比較.
* 傳回結果為true需要滿足兩個條件:
* 1.傳入對象類型為map類型
* 2.傳入對象和目前map鍵值對完全一緻.
* 更标準的講,兩個map的entrySet在equals結果為true,則表示鍵值對一緻.這才能保證對于實作map接口的不同類在
* equals方法調用過程中正常使用.
*/
boolean equals(Object o);
/**
* 傳回map的哈希值.
* map的哈希值=sum(每一個entry的哈希值).
* map的哈希值求法保證了:如果map1.equals(map2)結果為true,則map1.hashCode()=map2.hashCode(),
* 當然這樣的設計也符合Object中對hashCode的定義.
* @see Map.Entry#hashCode()
* @see Object#equals(Object)
* @see #equals(Object)
*/
int hashCode();
/*---------以下為預設方法--------*/
/**
* 傳回指定key對應的value,如果沒有對應的映射,則傳回傳入參數中的預設值defaultValue.
* @implSpec
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
* @since 1.8
*/
default V getOrDefault(Object key, V defaultValue) {
V v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
/**
* 對map中每一個entry執行action中定義對操作,直到全部entry執行完成or執行中出現異常為止.
* 除非map的實作類有規定,否則forEach的執行順序為entrySet中entry的順序.
* 執行中的異常最終抛給方法的調用者.
* @implSpec
* The default implementation is equivalent to, for this {@code map}:
* 本方法的預設實作和下面的代碼是等價的:
* for (Map.Entry<K, V> entry : map.entrySet())
* action.accept(entry.getKey(), entry.getValue());
* }
*
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
*
* @since 1.8
*/
default void forEach(BiConsumer<? super K, ? super V> action) {
Objects.requireNonNull(action);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
action.accept(k, v);
}
}
/**
* 對于map中每一個entry,将其value替換成BiFunction接口傳回的值.直到所有entry替換完or出現異常為止.
* 如果執行過程中出現異常,則抛給調用者.
* @implSpec
* 本方法的預設執行和下面代碼等價:
* for (Map.Entry<K, V> entry : map.entrySet())
* entry.setValue(function.apply(entry.getKey(), entry.getValue()));
* }
*
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
*
* @since 1.8
*/
default void replaceAll(java.util.function.BiFunction<? super K, ? super V, ? extends V> function) {
Objects.requireNonNull(function);
for (Map.Entry<K, V> entry : entrySet()) {
K k;
V v;
try {
k = entry.getKey();
v = entry.getValue();
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
// ise thrown from function is not a cme.
v = function.apply(k, v);
try {
entry.setValue(v);
} catch(IllegalStateException ise) {
// this usually means the entry is no longer in the map.
throw new ConcurrentModificationException(ise);
}
}
}
/**
* 如果指定的鍵尚未與值相關聯(或被映射為null),則将它與給定的值相關聯并傳回null,否則傳回目前值。
* @implSpec
* 本方法的預設執行和下面的代碼等價:
* V v = map.get(key);
* if (v == null)
* v = map.put(key, value);
* return v;
* }
*
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
*
* @since 1.8
*/
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
/**
* 如果給定的參數key和value在map中是一個entry,則删除這個entry.
* @implSpec
* 本方法的預設實作和下面代碼等價:
* if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
* map.remove(key);
* return true;
* } else
* return false;
* }
*
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
*
* @since 1.8
*/
default boolean remove(Object key, Object value) {
Object curValue = get(key);
if (!Objects.equals(curValue, value) ||
(curValue == null && !containsKey(key))) {
return false;
}
remove(key);
return true;
}
/**
* Replaces the entry for the specified key only if currently
* mapped to the specified value.
* 如果給定的key和value在map中有entry,則為指定key的entry,用新value替換舊的value.
* @implSpec
* 本方法的預設實作和下面代碼等價:
* if (map.containsKey(key) && Objects.equals(map.get(key), value)) {
* map.put(key, newValue);
* return true;
* } else
* return false;
* }
*
* 對于不支援value為null的map來說,如果舊value為null,本方法預設不抛出異常,除非新value也是null.
*
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
* @since 1.8
*/
default boolean replace(K key, V oldValue, V newValue) {
Object curValue = get(key);
if (!Objects.equals(curValue, oldValue) ||
(curValue == null && !containsKey(key))) {
return false;
}
put(key, newValue);
return true;
}
/**
* 如果指定key在map中有value,則用參數value進行替換.
* 本方法的預設實作和下面代碼等價:
* if (map.containsKey(key)) {
* return map.put(key, value);
* } else
* return null;
* }
*
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
*
* @since 1.8
*/
default V replace(K key, V value) {
V curValue;
if (((curValue = get(key)) != null) || containsKey(key)) {
curValue = put(key, value);
}
return curValue;
}
/**
* 如果指定key在map中沒有對應的value,則使用輸入參數,即函數接口mappingfunction為其計算一個value.
* 如果計算value不為null,則将value插入map中.
*
* 如果計算function傳回結果為null,則不插入任何映射.如果函數function本身抛出(非檢查型)異常,則異常會被重新抛出,自然
* 也不會插入任何映射.
* 最常見的用法是構造一個新的對象作為初始映射值或memoized結果,如下所示:
* map.computeIfAbsent(key, k -> new Value(f(k)));
*
* or是為了實作一個多value的map(如Map<k,Collection<v>>),進而支援一個key對應多個value.
*
* map.computeIfAbsent(key, k -> new HashSet<V>()).add(v);
* }</pre>
*
* 本方法的預設實作和下面代碼等價:
* if (map.get(key) == null) {
* V newValue = mappingFunction.apply(key);
* if (newValue != null)
* map.put(key, newValue);
* }
* }
*
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
* 特别是,所有ConcurrentMap的子接類,必須給出說明:如果目前value不存在,指派的function操作是否為原子性的.
* @since 1.8
*/
default V computeIfAbsent(K key,
java.util.function.Function<? super K, ? extends V> mappingFunction) {
Objects.requireNonNull(mappingFunction);
V v;
if ((v = get(key)) == null) {
V newValue;
if ((newValue = mappingFunction.apply(key)) != null) {
put(key, newValue);
return newValue;
}
}
return v;
}
/**
* 如果map中存在指定key對應的value,且不為null,則本方法會嘗試使用function,并利用key生成一個新的value.
*
* 如果function接口傳回null,map中原entry則被移除.如果function本身抛出異常,則目前map不會發生改變.
*
* 本方法的預設實作和下面的代碼等價:
* if (map.get(key) != null) {
* V oldValue = map.get(key);
* V newValue = remappingFunction.apply(key, oldValue);
* if (newValue != null)
* map.put(key, newValue);
* else
* map.remove(key);
* }
* }
*
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
* 特别是,所有ConcurrentMap的子接類,必須給出說明:如果目前value不存在,指派的function操作是否為原子性的.
*
* @since 1.8
*/
default V computeIfPresent(K key,
java.util.function.BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
V oldValue;
if ((oldValue = get(key)) != null) {
V newValue = remappingFunction.apply(key, oldValue);
//如果新value不為null,則更新value
if (newValue != null) {
put(key, newValue);
return newValue;
}
//如果新value為null,則删除map中原entry
else {
remove(key);
return null;
}
} else {
return null;
}
}
/**
* 利用指定key和value計算一個新映射.
* 比如:向一個value映射中新增或者拼接一個String.
*
* 下面的merge()方法在這樣的用途上面通常使用更友善一些.
*
* 如果function接口傳回null,則map中原entry被移除(如果本來就不存在,則不執行移除操作).
* 如果function本身抛出(非檢查型)異常,異常會被重新抛出,目前map也不會發生改變.
*
* 本方法的預設實作和下面代碼等價:
* V oldValue = map.get(key);
* V newValue = remappingFunction.apply(key, oldValue);
* if (oldValue != null ) {
* if (newValue != null)
* map.put(key, newValue);
* else
* map.remove(key);
* } else {
* if (newValue != null)
* map.put(key, newValue);
* else
* return null;
* }
* }
*
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
* 特别是,所有ConcurrentMap的子接類,必須給出說明:如果目前value不存在,指派的function操作是否為原子性的.
*
* @since 1.8
*/
default V compute(K key,
java.util.function.BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
//擷取舊value
V oldValue = get(key);
//利用key和舊value生成新value
V newValue = remappingFunction.apply(key, oldValue);
//如果新value為null
if (newValue == null) {
//如果舊value不為null,或者map存在key,則删除舊entry
if (oldValue != null || containsKey(key)) {
// something to remove
remove(key);
return null;
} else {
//什麼也不做
return null;
}
} else {
// 添加or覆寫原映射
put(key, newValue);
return newValue;
}
}
/**
* 如果指定key沒有value,或者其value為null,則将其改為給定的非null的value.
* 否則,用給定的function傳回值替換原value.
* 如果給定參數value和function傳回結果都為null,則删除map中這個entry.
* 這一方法常用于:對一個key合并多個映射的value時.
* 比如:要建立或追加一個String給一個值映射.
*
* 如果function傳回null,則map中原entry被移除.如果function本身抛出異常,則異常會被重新抛出,且
* 目前map不會發生更改.
*
* 本方法的預設實作和下面代碼等價,傳回結果為目前value或者null
* V oldValue = map.get(key);
* V newValue = (oldValue == null) ? value :
* remappingFunction.apply(oldValue, value);
* if (newValue == null)
* map.remove(key);
* else
* map.put(key, newValue);
* }
*
* 此預設方法對于線程同步和執行過程的原子性并不能保證.
* 任何實施提供原子性保證必須重寫此方法并記錄其方法.
* 特别是,所有ConcurrentMap的子接類,必須給出說明:如果目前value不存在,指派的function操作是否為原子性的
*
* @since 1.8
*/
default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
//擷取舊value
V oldValue = get(key);
//如果舊value為null,則newValue=參數value;
//如果舊value不為null,則newValue=function接口傳回值
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue, value);
//如果newValue=null,則删除map中的entry,說明參數value=null或者function接口傳回值為null
if(newValue == null) {
remove(key);
}
//否則,為原entry插入新value.
else {
put(key, newValue);
}
//傳回新value
return newValue;
}
}
參考:https://blog.csdn.net/caoxiaohong1005/article/category/7278825