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

Java中LinkedHashSet的源碼分析

 更新時(shí)間:2023年09月05日 09:52:04   作者:_雨_  
這篇文章主要介紹了Java中LinkedHashSet的源碼分析,LinkedHashSet 是 Java 中的一個(gè)集合類,它是 HashSet 的子類,同時(shí)也實(shí)現(xiàn)了 Set 接口,與 HashSet 不同的是,LinkedHashSet 保留了元素插入的順序,并且具有 HashSet 的快速查找特性,需要的朋友可以參考下

LinkedHashSet的基本介紹

LinkedHashSet 是 Java 中的一個(gè)集合類,它是 HashSet 的子類,同時(shí)也實(shí)現(xiàn)了 Set 接口。與 HashSet 不同的是,LinkedHashSet 保留了元素插入的順序,并且具有 HashSet 的快速查找特性。

LinkedHashSet 繼承了 HashSet,所以它是在 HashSet 的基礎(chǔ)上維護(hù)了元素添加順序的功能。

它的構(gòu)造方法是 LinkedHashSet()。 LinkedHashSet 是一個(gè)基于 LinkedHashMap 實(shí)現(xiàn)的有序去重集合列表。

它中的元素沒有重復(fù),有順序,并且可以存儲(chǔ) null 值。

需要注意的是,LinkedHashSet 是一個(gè)線程不安全的容器。

  1. LinkedHashSet是HashSet的子類
  2. LinkedHashSet底層是一個(gè)LinkedHashMap,底層維護(hù)了一個(gè)數(shù)組+雙向鏈表
  3. LinkedHashSet 根據(jù)元素的hashCode值來決定元素的存儲(chǔ)位置,同時(shí)使用鏈表維護(hù)元素的次序(圖),這使得元素看起來是以插入順序保存的。
  4. LinkedHashSet不允許添重復(fù)元素

LinkedHashSet源碼分析

1.在LinkedHastSet 中維護(hù)了一個(gè)hash表和雙向鏈表(LinkedHashSet有head和tail)

2.每一個(gè)節(jié)點(diǎn)有pre和next屬性,這樣可以形成雙向鏈表

3.在添加一個(gè)元素時(shí),先求hash值,在求索引.,確定該元素在hashtable的位置,然后將添加的元素加入到雙向鏈表(如果已經(jīng)存在,不添加[原則和hashset一樣])

tail.next=newElement//簡(jiǎn)單指定
newElement.pre=tail
tail = newEelment;

 4)這樣的話,我們遍歷LinkedHashSet也能確保插入順序和遍歷順序一致

源碼分析

因?yàn)長(zhǎng)inkedHashSet是HashSet的子類,因此在底層使用的方法,還是HashSet的方法,因?yàn)镠ashSet的底層是HashMap,所以最終走的還是HashMap的putVal方法

可以參考putVal方法,我們?cè)趯ashSet的時(shí)候已經(jīng)詳細(xì)的解釋過了

/*
對(duì)HashSet 的源碼解讀
1. 執(zhí)行 HashSet()
    public HashSet() {
        map = new HashMap<>();
    }
2. 執(zhí)行 add()
   public boolean add(E e) {//e = "java"
        //這里的PRESENT 是一個(gè)空對(duì)象數(shù)組,起到占位符作用
        return map.put(e, PRESENT)==null;//(static) PRESENT = new Object();
   }
 3.執(zhí)行 put() , 該方法會(huì)執(zhí)行 hash(key) 得到key對(duì)應(yīng)的hash值 算法h = key.hashCode()) ^ (h >>> 16)
   根據(jù)我們傳入進(jìn)來的值,去計(jì)算hash值,在Table中存放的位置
     public V put(K key, V value) {//key = "java" value = PRESENT 共享
        return putVal(hash(key), key, value, false, true);
    }
 4.執(zhí)行 putVal
 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
           boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i; //定義了輔助變量
        //table 就是 HashMap 的一個(gè)數(shù)組,類型是 Node[]
        //if 語句表示如果當(dāng)前table 是null, 或者 大小=0
        //就會(huì)進(jìn)行第一次擴(kuò)容,到16個(gè)空間.
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //(1)根據(jù)key,得到hash 去計(jì)算該key應(yīng)該存放到table表的哪個(gè)索引位置
        //并把這個(gè)位置的對(duì)象,賦給 p
        //(2)判斷p 是否為null
        //(2.1) 如果p 為null, 表示還沒有存放元素, 就創(chuàng)建一個(gè)Node (key="java",value=PRESENT)
        //(2.2) 就放在該位置 tab[i] = newNode(hash, key, value, null) 就是直接存放進(jìn)去
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            //一個(gè)開發(fā)技巧提示: 在需要局部變量(輔助變量)時(shí)候,在創(chuàng)建
            Node<K,V> e; K k; //
            //當(dāng)進(jìn)入else的時(shí)候,就說明我們當(dāng)前計(jì)算出來的Hash值在數(shù)組中的位置已經(jīng)存在了 ,那么就先進(jìn)行判斷
            //如果當(dāng)前索引位置對(duì)應(yīng)的鏈表的第一個(gè)元素的hash值和準(zhǔn)備添加的key的hash值一樣
            //并且滿足 下面兩個(gè)條件之一:
            //(1) 準(zhǔn)備加入的keyhash值 和 p 指向的Node 結(jié)點(diǎn)的hash值相同,那就說明是是同一個(gè)對(duì)象
            //(2)  當(dāng)前的key對(duì)象 或者和我們傳入對(duì)象的地址相同,因?yàn)?=在判斷引用類型的時(shí)候,判斷的是地址是否相同,如果地址相同,或者他們的內(nèi)容相同
            //就不能加入     如果不能加入就把p賦給e
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //再判斷 p 是不是一顆紅黑樹,
            //如果是一顆紅黑樹,就調(diào)用 putTreeVal , 來進(jìn)行添加
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                //如果上面的情況都不是的話,那么就說明,此時(shí)這個(gè)索引對(duì)應(yīng)的位置是一個(gè)鏈表了
            else {//如果table對(duì)應(yīng)索引位置,已經(jīng)是一個(gè)鏈表, 就使用for循環(huán)比較
                  //(1) 依次和該鏈表的每一個(gè)元素比較后,都不相同, 則加入到該鏈表的最后
                  //    注意在把元素添加到鏈表后,立即判斷 該鏈表是否已經(jīng)達(dá)到8個(gè)結(jié)點(diǎn)
                  //    , 就調(diào)用 treeifyBin() 對(duì)當(dāng)前這個(gè)鏈表進(jìn)行樹化(轉(zhuǎn)成紅黑樹)
                  //    注意,在轉(zhuǎn)成紅黑樹時(shí),要進(jìn)行判斷, 判斷條件
                  //    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
                  //            resize();
                  //    如果上面條件成立,先table擴(kuò)容.
                  //    只有上面條件不成立時(shí),才進(jìn)行轉(zhuǎn)成紅黑樹
                  //(2) 依次和該鏈表的每一個(gè)元素比較過程中,如果有相同情況,就直接break
                //這是一個(gè)死循環(huán),會(huì)一直進(jìn)行 比較,只有兩種情況,才會(huì)退出循環(huán)
                //第一種:當(dāng)數(shù)組中的其中一條列表的長(zhǎng)度到達(dá)了7,準(zhǔn)備進(jìn)行樹化的時(shí)候
                //第二種:就是發(fā)現(xiàn)我們加入的元素,在這個(gè)列表中發(fā)現(xiàn)了重復(fù)的,也會(huì)直接跳出循環(huán)
                for (int binCount = 0; ; ++binCount) {
                        這里e=p.next 因?yàn)槲覀冊(cè)谧铋_始上面的時(shí)候,已經(jīng)對(duì)第一個(gè)元素進(jìn)行了判斷,所以這里直接從下一個(gè)元素開始判斷
                        如果下一個(gè)元素為空,那么就直接加入
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        加入完一個(gè)元素之后,馬上的進(jìn)行判斷,當(dāng)前列表的個(gè)數(shù)有幾個(gè),是否進(jìn)行樹化
                        if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //這里就是,在循環(huán)比較的過程中,如果發(fā)現(xiàn)有相同的內(nèi)容,那么會(huì)直接break
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                       //這里就是讓p 指向下一個(gè)
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        //size 就是我們每加入一個(gè)結(jié)點(diǎn)Node(k,v,h,next), size++
        if (++size > threshold)
            resize();//擴(kuò)容
        afterNodeInsertion(evict);
        return null;
    }
 */
package idea.chapter14.set_;
import java.util.LinkedHashSet;
import java.util.Set;
@SuppressWarnings({"all"})
public class LinkedHashSetSource {
    public static void main(String[] args) {
        //分析一下LinkedHashSet的底層機(jī)制
        Set set = new LinkedHashSet();
        set.add(new String("AA"));
        set.add(456);
        set.add(456);
        set.add(new Customer("劉", 1001));
        set.add(123);
        System.out.println("set=" + set);
        //源碼分析
        //1. LinkedHashSet 加入順序和取出元素/數(shù)據(jù)的順序一致,因?yàn)長(zhǎng)inkedHashSet在底層維護(hù)了一個(gè)雙向鏈表+數(shù)組,因此可以保證數(shù)據(jù)取出的順序保持一致
        //2. LinkedHashSet 底層維護(hù)的是一個(gè)LinkedHashMap(是HashMap的子類)
        //3. LinkedHashSet 底層結(jié)構(gòu) (數(shù)組table+雙向鏈表)
        //4. 添加第一次時(shí),直接將 數(shù)組table 擴(kuò)容到 16 ,存放的結(jié)點(diǎn)類型是 LinkedHashMap$Entry
        //5. 數(shù)組是 HashMap$Node[] 存放的元素/數(shù)據(jù)是 LinkedHashMap$Entry類型
        /*
                //繼承關(guān)系是在內(nèi)部類完成.
                static class Entry<K,V> extends HashMap.Node<K,V> {
                    Entry<K,V> before, after;
                    Entry(int hash, K key, V value, Node<K,V> next) {
                        super(hash, key, value, next);
                    }
                }
         */
    }
}
class Customer {
    private String name;
    private int no;
    public Customer(String name, int no) {
        this.name = name;
        this.no = no;
    }
}

LinkedHashset練習(xí)

代碼演示:

在沒有重寫equals方法和hashcode方法的時(shí)候都是可以加入進(jìn)去的,因?yàn)槎际莿?chuàng)建出來新的對(duì)象

package idea.chapter14.set_;
import java.util.LinkedHashSet;
import java.util.Objects;
/*
Car類(屬性:name,price), 如果name和price一樣,
。則認(rèn)為是相同元素,就不能添加。5min. I
 */
@SuppressWarnings({"all"})
public class LinkedHashSetExercise {
    public static void main(String[] args) {
        LinkedHashSet linkedHashSet = new LinkedHashSet();
        linkedHashSet.add(new Car("奧拓", 1000));//OK
        linkedHashSet.add(new Car("奧迪", 300000));//OK
        linkedHashSet.add(new Car("法拉利", 10000000));//OK
        linkedHashSet.add(new Car("奧迪", 300000));//加入不了
        linkedHashSet.add(new Car("保時(shí)捷", 70000000));//OK
        linkedHashSet.add(new Car("奧迪", 300000));//加入不了
        System.out.println(linkedHashSet);
    }
}
class Car {
    private String name;
    private double price;
    public Car(String name, double price) {
        this.name = name;
        this.price = price;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
    @Override
    public String toString() {
        return "\nCar{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}

到此這篇關(guān)于Java中LinkedHashSet的源碼分析的文章就介紹到這了,更多相關(guān)LinkedHashSet的源碼內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用RestTemplate訪問https實(shí)現(xiàn)SSL請(qǐng)求操作

    使用RestTemplate訪問https實(shí)現(xiàn)SSL請(qǐng)求操作

    這篇文章主要介紹了使用RestTemplate訪問https實(shí)現(xiàn)SSL請(qǐng)求操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java?Thread.currentThread().getName()?和?this.getName()區(qū)別詳解

    Java?Thread.currentThread().getName()?和?this.getName()區(qū)別詳

    本文主要介紹了Thread.currentThread().getName()?和?this.getName()區(qū)別詳解,TestThread?testThread?=?new?TestThread();
    2022-02-02
  • CyclicBarrier線程同步共享變量底層原理示例解析

    CyclicBarrier線程同步共享變量底層原理示例解析

    這篇文章主要為大家介紹了CyclicBarrier線程同步共享變量底層原理示例解析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 一文讀懂Java多線程背后的故事

    一文讀懂Java多線程背后的故事

    Java是一種廣泛使用的編程語言,而多線程是Java程序員必不可少的一部分,Java的多線程支持具有確保數(shù)據(jù)同步、最大化利用CPU資源、并行處理任務(wù)等眾多優(yōu)點(diǎn),本文從實(shí)際應(yīng)用場(chǎng)景出發(fā),為您詳細(xì)介紹 Java 多線程的各個(gè)方面的實(shí)際應(yīng)用及背景
    2023-06-06
  • Java數(shù)據(jù)結(jié)構(gòu)與算法之雙向鏈表、環(huán)形鏈表及約瑟夫問題深入理解

    Java數(shù)據(jù)結(jié)構(gòu)與算法之雙向鏈表、環(huán)形鏈表及約瑟夫問題深入理解

    這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)與算法之雙向鏈表、環(huán)形鏈表及約瑟夫問題深入理解,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • SpringMVC整合kinfe4j及問題解決分析

    SpringMVC整合kinfe4j及問題解決分析

    這篇文章主要為大家介紹了SpringMVC整合kinfe4j及問題解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • java實(shí)現(xiàn)通用分頁(后端)

    java實(shí)現(xiàn)通用分頁(后端)

    這篇文章主要介紹了java實(shí)現(xiàn)通用分頁(后端)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • spring Cloud微服務(wù)跨域?qū)崿F(xiàn)步驟

    spring Cloud微服務(wù)跨域?qū)崿F(xiàn)步驟

    這篇文章主要介紹了spring Cloud微服務(wù)跨域?qū)崿F(xiàn)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • java實(shí)現(xiàn)Img與PDF相互轉(zhuǎn)換

    java實(shí)現(xiàn)Img與PDF相互轉(zhuǎn)換

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)Img與PDF相互轉(zhuǎn)換的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • java 將一個(gè)數(shù)組逆序輸出的方法

    java 將一個(gè)數(shù)組逆序輸出的方法

    今天小編就為大家分享一篇java 將一個(gè)數(shù)組逆序輸出的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2018-06-06

最新評(píng)論