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

Java中LinkedHashSet的源碼分析

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

LinkedHashSet的基本介紹

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

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

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

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

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

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

LinkedHashSet源碼分析

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

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

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

tail.next=newElement//簡單指定
newElement.pre=tail
tail = newEelment;

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

源碼分析

因為LinkedHashSet是HashSet的子類,因此在底層使用的方法,還是HashSet的方法,因為HashSet的底層是HashMap,所以最終走的還是HashMap的putVal方法

可以參考putVal方法,我們在將HashSet的時候已經(jīng)詳細的解釋過了

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

package idea.chapter14.set_;
import java.util.LinkedHashSet;
import java.util.Objects;
/*
Car類(屬性:name,price), 如果name和price一樣,
。則認為是相同元素,就不能添加。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("保時捷", 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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用RestTemplate訪問https實現(xiàn)SSL請求操作

    使用RestTemplate訪問https實現(xiàn)SSL請求操作

    這篇文章主要介紹了使用RestTemplate訪問https實現(xiàn)SSL請求操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    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線程同步共享變量底層原理示例解析詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-07-07
  • 一文讀懂Java多線程背后的故事

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

    Java是一種廣泛使用的編程語言,而多線程是Java程序員必不可少的一部分,Java的多線程支持具有確保數(shù)據(jù)同步、最大化利用CPU資源、并行處理任務(wù)等眾多優(yōu)點,本文從實際應(yīng)用場景出發(fā),為您詳細介紹 Java 多線程的各個方面的實際應(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)形鏈表及約瑟夫問題深入理解,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-09-09
  • SpringMVC整合kinfe4j及問題解決分析

    SpringMVC整合kinfe4j及問題解決分析

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

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

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

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

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

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

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

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

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

最新評論