欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

為何HashSet中使用PRESENT而不是null作為value

 更新時間:2022年10月12日 15:16:04   作者:桐花思雨  
這篇文章主要介紹了為何HashSet中使用PRESENT而不是null作為value,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

1. 為什么 HashSet 中使用 PRESENT 而不是 null 作為 value

無意之中碰到了這個問題,在此記錄一下

1.1. PRESENT 是個什么玩意

HashSet 的部分源碼如下

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable {
? ??
? ? static final long serialVersionUID = -5024744406713321676L;
? ? private transient HashMap<E,Object> map;
? ? // Dummy value to associate with an Object in the backing Map
? ? private static final Object PRESENT = new Object();
}

1.2. HashSet 的構造方法

// 默認構造函數(shù) 底層創(chuàng)建一個HashMap
public HashSet() {
?? ?// 調用HashMap的默認構造函數(shù),創(chuàng)建map
? ? map = new HashMap<E,Object>();
}
?
// 帶集合的構造函數(shù)
public HashSet(Collection<? extends E> c) {
?? ?// 創(chuàng)建map。
? ? map = new HashMap<E,Object>(Math.max((int) (c.size()/.75f) + 1, 16));
? ? // 將集合(c)中的全部元素添加到HashSet中
? ? addAll(c);
}
?
// 指定HashSet初始容量和加載因子的構造函數(shù)
public HashSet(int initialCapacity, float loadFactor) {
?? ?map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
?
// 指定HashSet初始容量的構造函數(shù)
public HashSet(int initialCapacity) {
?? ?map = new HashMap<E,Object>(initialCapacity);
}
?
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
?? ?map = new LinkedHashMap<E,Object>(initialCapacity, loadFactor);
}

1.3. PRESENT 何時會被用到

  • add(E) 方法
  • remove(Object) 方法

1.3.1. HashSet 中的 add(E) 方法

/**
?* add(E) 方法返回 null 時,表示 HashSet 添加數(shù)據(jù)成功
?*
?* @return true 如果不包含該元素
?*/
public boolean add(E e) {
?? ?return map.put(e, PRESENT)==null;
}

直接調用的是 HashMap 的 put(K, V) 方法,此時傳入的 value 值是 PRESENT

public V put(K key, V value) {
	return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
    // tab表示 Node<K,V>類型的數(shù)組,p表示某一個具體的單鏈表 Node<K,V> 節(jié)點               
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // 判斷 table[] 是否為空,如果是空的就創(chuàng)建一個 table[],并獲取他的長度n
    if ((tab = table) == null || (n = tab.length) == 0)
    	n = (tab = resize()).length;	
    // tab[i = (n - 1) & hash] 表示數(shù)組中的某一個具體位置的數(shù)據(jù)	
    // 如果單鏈表 Node<K,V> p == tab[i = (n - 1) & hash]) == null,
    // 就直接 put 進單鏈表中,說明此時并沒有發(fā)生 Hash 沖突
    if ((p = tab[i = (n - 1) & hash]) == null)
    	tab[i] = newNode(hash, key, value, null);
    else {
		// 說明索引位置已經(jīng)放入過數(shù)據(jù)了,已經(jīng)在單鏈表處產(chǎn)生了Hash沖突
        Node<K,V> e; K k;
		// 判斷 put 的數(shù)據(jù)和之前的數(shù)據(jù)是否重復
        if (p.hash == hash &&
            // 進行 key 的 hash 值和 key 的 equals() 和 == 比較,如果都相等,則初始化數(shù)組 Node<K,V> e
            ((k = p.key) == key || (key != null && key.equals(k))))   			
            e = p;
		// 判斷是否是紅黑樹,如果是紅黑樹就直接插入樹中
        else if (p instanceof TreeNode)
        	e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
			// 如果不是紅黑樹,就遍歷每個節(jié)點,判斷單鏈表長度是否大于等于 7,
			// 如果單鏈表長度大于等于 7,數(shù)組的長度小于 64 時,會優(yōu)先選擇擴容
			// 如果單鏈表長度大于等于 7,數(shù)組的長度大于 64 時,才會選擇單鏈表--->紅黑樹
            for (int binCount = 0; ; ++binCount) {
            	if ((e = p.next) == null) {
            		// 采用尾插法,在單鏈表中插入數(shù)據(jù)
                	p.next = newNode(hash, key, value, null);
                	// 如果 binCount >= 8 - 1
                    if (binCount >= TREEIFY_THRESHOLD - 1) 
                    	treeifyBin(tab, hash);
                        break;
                }
				// 判斷索引每個元素的key是否可要插入的key相同,如果相同就直接覆蓋
                if (e.hash == hash &&
					((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                 p = e;
			}
		}		
        if (e != null) { 
        	// 此時說明 key 的 hash 值和 key 的 equals() 和 == 比較結果都相等
        	// 說明數(shù)組或者單鏈表中有完全相同的 key
        	// 因此只需要將value覆蓋,并將oldValue返回即可
        	V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
            	e.value = value;
                afterNodeAccess(e);
              	return oldValue;
        }
	}
	// 說明沒有key相同,因此要插入一個key-value,并記錄內部結構變化次數(shù)
    ++modCount;
    // 判斷是否擴容
    if (++size > threshold)
    	resize();
    afterNodeInsertion(evict);
    return null;
}

關于 HashMap 的 put(K, V) 方法的 詳細解析請看這里

  • 如果 return oldValue 說明發(fā)生了 value 覆蓋,也就是說此時返回了 PRESENT,自然而然 HashMap 添加數(shù)據(jù)失敗
  • 如果 return null 說明 HashMap 添加數(shù)據(jù)成功

而如果將 PRESENT 替換為 null 作為 value 值,那么 HashSet 的 add(E) 方法將無法判斷添加元素的成功與失??;因為不管是成功與失敗都會返回結果 null

1.3.2. HashMap 進行 put 元素示例

1.3.3. HashSet 中的 remove(Object) 方法

HashSet 的 remove(Object) 方法源碼

public boolean remove(Object o) {
?? ?return map.remove(o)==PRESENT;
}

HashSet 的 remove(Object) 依舊直接使用 HashMap 的 remove(Object) 方法

public V remove(Object key) {
?? ?Node<K,V> e;
? ? return (e = removeNode(hash(key), key, null, false, true)) == null ?
?? ??? ?null : e.value;
}

HashMap 的 remove(Object) 方法會返回 null 或 value

  • 有該值,返回 value 也就是 PRESENT,表示 remove 成功
  • 無該值,返回 null,自然而然 remove 失敗

而如果將 PRESENT 替換為 null 作為 value 值,那么 HashSet 的 remove(Object) 方法將無法判斷移除元素的成功與失?。灰驗椴还苁浅晒εc失敗都會返回結果 null

以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。

相關文章

  • Java日期與時間類原理解析

    Java日期與時間類原理解析

    這篇文章主要介紹了Java日期與時間類原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-01-01
  • hibernate-validator改進校驗框架validator?v0.4使用

    hibernate-validator改進校驗框架validator?v0.4使用

    這篇文章主要為大家介紹了改進?hibernate-validator,新一代校驗框架?validator?使用介紹?v0.4,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪<BR>
    2023-03-03
  • java Class.getSimpleName() 詳解及用法

    java Class.getSimpleName() 詳解及用法

    這篇文章主要介紹了java Class.getSimpleName() 詳解及用法的相關資料,需要的朋友可以參考下
    2017-02-02
  • java中接口(interface)及使用方法示例

    java中接口(interface)及使用方法示例

    這篇文章主要介紹了java中接口(interface)及使用方法示例,涉及接口定義的簡單介紹以及Java語言代碼示例,具有一定借鑒價值,需要的朋友可以參考下。
    2017-11-11
  • java的Jackson將json字符串轉換成泛型List

    java的Jackson將json字符串轉換成泛型List

    這篇文章主要介紹了java的Jackson將json字符串轉換成泛型List ,這里整理了詳細的代碼,有需要的小伙伴可以參考下。
    2017-02-02
  • Springcloud中Feign傳遞參數(shù)的過程解析

    Springcloud中Feign傳遞參數(shù)的過程解析

    這篇文章主要介紹了Springcloud中Feign傳遞參數(shù)的過程,單個參數(shù)的傳值有兩種方式,第一種使用@RequestParam/@PathVariable進行傳值,傳遞多個參數(shù):多個參數(shù)的傳值可以使用多個@RequestParam來進行傳參,需要的朋友可以參考下
    2023-09-09
  • 入門到精通Java?SSO單點登錄原理詳解

    入門到精通Java?SSO單點登錄原理詳解

    這篇文章主要介紹了入門到精通Java?SSO單點登錄原理詳解,本文主要對SSO單點登錄與CAS、OAuth2.0兩種授權協(xié)議的關系和原理進行詳細說明
    2022-09-09
  • Java swing仿酷狗音樂播放器

    Java swing仿酷狗音樂播放器

    這篇文章主要為大家詳細介紹了Java swing實現(xiàn)音樂播放器,Java開發(fā)圖形界面程序音樂播放器仿酷狗音樂播放器,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • spring項目實現(xiàn)單元測試過程解析

    spring項目實現(xiàn)單元測試過程解析

    這篇文章主要介紹了spring項目實現(xiàn)單元測試過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-10-10
  • java寫入文件的幾種方法分享

    java寫入文件的幾種方法分享

    這篇文章主要介紹了java寫入文件的幾種方法,需要的朋友可以參考下
    2014-02-02

最新評論