最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

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

1.1. PRESENT 是個(gè)什么玩意

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 的構(gòu)造方法

// 默認(rèn)構(gòu)造函數(shù) 底層創(chuàng)建一個(gè)HashMap
public HashSet() {
?? ?// 調(diào)用HashMap的默認(rèn)構(gòu)造函數(shù),創(chuàng)建map
? ? map = new HashMap<E,Object>();
}
?
// 帶集合的構(gòu)造函數(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初始容量和加載因子的構(gòu)造函數(shù)
public HashSet(int initialCapacity, float loadFactor) {
?? ?map = new HashMap<E,Object>(initialCapacity, loadFactor);
}
?
// 指定HashSet初始容量的構(gòu)造函數(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 何時(shí)會(huì)被用到

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

1.3.1. HashSet 中的 add(E) 方法

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

直接調(diào)用的是 HashMap 的 put(K, V) 方法,此時(shí)傳入的 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表示某一個(gè)具體的單鏈表 Node<K,V> 節(jié)點(diǎn)               
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // 判斷 table[] 是否為空,如果是空的就創(chuàng)建一個(gè) table[],并獲取他的長度n
    if ((tab = table) == null || (n = tab.length) == 0)
    	n = (tab = resize()).length;	
    // tab[i = (n - 1) & hash] 表示數(shù)組中的某一個(gè)具體位置的數(shù)據(jù)	
    // 如果單鏈表 Node<K,V> p == tab[i = (n - 1) & hash]) == null,
    // 就直接 put 進(jìn)單鏈表中,說明此時(shí)并沒有發(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ù)是否重復(fù)
        if (p.hash == hash &&
            // 進(jìn)行 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 {
			// 如果不是紅黑樹,就遍歷每個(gè)節(jié)點(diǎn),判斷單鏈表長度是否大于等于 7,
			// 如果單鏈表長度大于等于 7,數(shù)組的長度小于 64 時(shí),會(huì)優(yōu)先選擇擴(kuò)容
			// 如果單鏈表長度大于等于 7,數(shù)組的長度大于 64 時(shí),才會(huì)選擇單鏈表--->紅黑樹
            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;
                }
				// 判斷索引每個(gè)元素的key是否可要插入的key相同,如果相同就直接覆蓋
                if (e.hash == hash &&
					((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                 p = e;
			}
		}		
        if (e != null) { 
        	// 此時(shí)說明 key 的 hash 值和 key 的 equals() 和 == 比較結(jié)果都相等
        	// 說明數(shù)組或者單鏈表中有完全相同的 key
        	// 因此只需要將value覆蓋,并將oldValue返回即可
        	V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
            	e.value = value;
                afterNodeAccess(e);
              	return oldValue;
        }
	}
	// 說明沒有key相同,因此要插入一個(gè)key-value,并記錄內(nèi)部結(jié)構(gòu)變化次數(shù)
    ++modCount;
    // 判斷是否擴(kuò)容
    if (++size > threshold)
    	resize();
    afterNodeInsertion(evict);
    return null;
}

關(guān)于 HashMap 的 put(K, V) 方法的 詳細(xì)解析請(qǐng)看這里

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

而如果將 PRESENT 替換為 null 作為 value 值,那么 HashSet 的 add(E) 方法將無法判斷添加元素的成功與失??;因?yàn)椴还苁浅晒εc失敗都會(huì)返回結(jié)果 null

1.3.2. HashMap 進(jìn)行 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) 方法會(huì)返回 null 或 value

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

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

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

相關(guān)文章

  • jmeter中beanshell的用法小結(jié)

    jmeter中beanshell的用法小結(jié)

    本文主要介紹了jmeter中beanshell的用法小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 如何在springboot中使用定時(shí)任務(wù)

    如何在springboot中使用定時(shí)任務(wù)

    這篇文章主要介紹了如何在springboot中使用定時(shí)任務(wù),幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下
    2020-12-12
  • Spring Boot如何使用HikariCP連接池詳解

    Spring Boot如何使用HikariCP連接池詳解

    這篇文章主要給大家介紹了關(guān)于Spring Boot如何使用HikariCP連接池的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者使用springboot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Netty簡單的入門代碼示例

    Netty簡單的入門代碼示例

    這篇文章主要介紹了Netty簡單的入門代碼示例,Netty 的內(nèi)部實(shí)現(xiàn)是很復(fù)雜的,但是 Netty 提供了簡單易用的API從網(wǎng)絡(luò)處理代碼中解耦業(yè)務(wù)邏輯,Netty 是完全基于 NIO 實(shí)現(xiàn)的,所以整個(gè) Netty 都是異步的,需要的朋友可以參考下
    2023-12-12
  • Java加載資源文件時(shí)的路徑問題的解決辦法

    Java加載資源文件時(shí)的路徑問題的解決辦法

    今天偶然看到一篇關(guān)于tomcat加載servlet的文章,不由得想起了java加載資源文件的路徑問題,資源文件可以使xml,properties,圖片等,可以是任何文件
    2013-04-04
  • Java文件上傳與文件下載實(shí)現(xiàn)方法詳解

    Java文件上傳與文件下載實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了Java文件上傳與文件下載實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了Java文件上傳與文件下載相關(guān)操作原理、實(shí)現(xiàn)方法及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-02-02
  • Java IO之File 類詳解

    Java IO之File 類詳解

    這篇文章主要為大家介紹了vue組件通信的幾種方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • Java語言實(shí)現(xiàn)掃雷游戲(1)

    Java語言實(shí)現(xiàn)掃雷游戲(1)

    這篇文章主要為大家詳細(xì)介紹了Java語言實(shí)現(xiàn)的掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04
  • java對(duì)于目錄下文件的單詞查找操作代碼實(shí)現(xiàn)

    java對(duì)于目錄下文件的單詞查找操作代碼實(shí)現(xiàn)

    這篇文章主要介紹了java對(duì)于目錄下文件的單詞查找操作代碼實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • java如何將map數(shù)據(jù)存入到實(shí)體類對(duì)象中

    java如何將map數(shù)據(jù)存入到實(shí)體類對(duì)象中

    在Java編程中,經(jīng)常需要將Map集合中的數(shù)據(jù)轉(zhuǎn)換為實(shí)體類對(duì)象,這可以通過反射機(jī)制實(shí)現(xiàn),即通過遍歷Map對(duì)象,使用反射根據(jù)鍵名對(duì)應(yīng)實(shí)體類的屬性名,動(dòng)態(tài)調(diào)用setter方法將值設(shè)置到實(shí)體對(duì)象中,這樣的操作使得數(shù)據(jù)從Map結(jié)構(gòu)轉(zhuǎn)移到了具體的JavaBean中,便于后續(xù)的操作和管理
    2024-09-09

最新評(píng)論

健康| 华容县| 恭城| 瑞安市| 冷水江市| 南开区| 蚌埠市| 尼木县| 昭通市| 鄂州市| 谷城县| 宁都县| 若尔盖县| 沙坪坝区| 瑞丽市| 沈阳市| 宣武区| 克拉玛依市| 英德市| 松江区| 盈江县| 荣昌县| 九江市| 昆山市| 高碑店市| 论坛| 安顺市| 黄山市| 藁城市| 西充县| 阿城市| 武平县| 西乌| 遵义市| 克东县| 界首市| 博湖县| 宜章县| 滦南县| 襄城县| 巴里|