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

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)的有序去重集合列表。

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

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

  1. LinkedHashSet是HashSet的子類
  2. LinkedHashSet底層是一個(gè)LinkedHashMap,底層維護(hù)了一個(gè)數(shù)組+雙向鏈表
  3. LinkedHashSet 根據(jù)元素的hashCode值來(lái)決定元素的存儲(chǔ)位置,同時(shí)使用鏈表維護(hù)元素的次序(圖),這使得元素看起來(lái)是以插入順序保存的。
  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ì)的解釋過(guò)了

/*
對(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)來(lái)的值,去計(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 語(yǔ)句表示如果當(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, 表示還沒(méi)有存放元素, 就創(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è)開(kāi)發(fā)技巧提示: 在需要局部變量(輔助變量)時(shí)候,在創(chuàng)建
            Node<K,V> e; K k; //
            //當(dāng)進(jìn)入else的時(shí)候,就說(shuō)明我們當(dāng)前計(jì)算出來(lái)的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值相同,那就說(shuō)明是是同一個(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 是不是一顆紅黑樹(shù),
            //如果是一顆紅黑樹(shù),就調(diào)用 putTreeVal , 來(lái)進(jìn)行添加
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                //如果上面的情況都不是的話,那么就說(shuō)明,此時(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)行樹(shù)化(轉(zhuǎn)成紅黑樹(shù))
                  //    注意,在轉(zhuǎn)成紅黑樹(shù)時(shí),要進(jìn)行判斷, 判斷條件
                  //    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
                  //            resize();
                  //    如果上面條件成立,先table擴(kuò)容.
                  //    只有上面條件不成立時(shí),才進(jìn)行轉(zhuǎn)成紅黑樹(shù)
                  //(2) 依次和該鏈表的每一個(gè)元素比較過(guò)程中,如果有相同情況,就直接break
                //這是一個(gè)死循環(huán),會(huì)一直進(jìn)行 比較,只有兩種情況,才會(huì)退出循環(huán)
                //第一種:當(dāng)數(shù)組中的其中一條列表的長(zhǎng)度到達(dá)了7,準(zhǔn)備進(jìn)行樹(shù)化的時(shí)候
                //第二種:就是發(fā)現(xiàn)我們加入的元素,在這個(gè)列表中發(fā)現(xiàn)了重復(fù)的,也會(huì)直接跳出循環(huán)
                for (int binCount = 0; ; ++binCount) {
                        這里e=p.next 因?yàn)槲覀冊(cè)谧铋_(kāi)始上面的時(shí)候,已經(jīng)對(duì)第一個(gè)元素進(jìn)行了判斷,所以這里直接從下一個(gè)元素開(kāi)始判斷
                        如果下一個(gè)元素為空,那么就直接加入
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        加入完一個(gè)元素之后,馬上的進(jìn)行判斷,當(dāng)前列表的個(gè)數(shù)有幾個(gè),是否進(jìn)行樹(shù)化
                        if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //這里就是,在循環(huán)比較的過(guò)程中,如果發(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í)

代碼演示:

在沒(méi)有重寫equals方法和hashcode方法的時(shí)候都是可以加入進(jìn)去的,因?yàn)槎际莿?chuàng)建出來(lái)新的對(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)文章

  • 基于Nacos實(shí)現(xiàn)SpringBoot動(dòng)態(tài)定時(shí)任務(wù)調(diào)度

    基于Nacos實(shí)現(xiàn)SpringBoot動(dòng)態(tài)定時(shí)任務(wù)調(diào)度

    本文主要介紹了在Spring Boot項(xiàng)目中使用Spring Scheduling實(shí)現(xiàn)定時(shí)任務(wù),并通過(guò)Nacos動(dòng)態(tài)配置Cron表達(dá)式實(shí)現(xiàn)任務(wù)的動(dòng)態(tài)變更,通過(guò)實(shí)現(xiàn)SchedulingConfigurer接口和訂閱Nacos配置變更事件,成功實(shí)現(xiàn)了任務(wù)的動(dòng)態(tài)調(diào)度和暫停
    2025-11-11
  • Spring Security獲取用戶認(rèn)證信息的實(shí)現(xiàn)流程

    Spring Security獲取用戶認(rèn)證信息的實(shí)現(xiàn)流程

    Spring Security是一個(gè)能夠?yàn)榛赟pring的企業(yè)應(yīng)用系統(tǒng)提供聲明式的安全訪問(wèn)控制解決方案的安全框架。它提供了一組可以在Spring應(yīng)用上下文中配置的Bean,充分利用了Spring IoC,DI和AOP功能,為應(yīng)用系統(tǒng)提供聲明式的安全訪問(wèn)控制功能
    2022-12-12
  • Java SpringMVC的@RequestMapping注解使用及說(shuō)明

    Java SpringMVC的@RequestMapping注解使用及說(shuō)明

    這篇文章主要介紹了Java SpringMVC的@RequestMapping注解使用及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • Spring中的兩種代理JDK和CGLIB的區(qū)別淺談

    Spring中的兩種代理JDK和CGLIB的區(qū)別淺談

    本篇文章中主要介紹了Spring中的兩種代理JDK和CGLIB的區(qū)別淺談,詳解的介紹了JDK和CGLIB的原理和方法,有需要的朋友可以了解一下
    2017-04-04
  • spring 重復(fù)注解和aop攔截的實(shí)現(xiàn)示例

    spring 重復(fù)注解和aop攔截的實(shí)現(xiàn)示例

    本文主要介紹了spring 重復(fù)注解和aop攔截的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • Java?IO流之StringWriter和StringReader用法分析

    Java?IO流之StringWriter和StringReader用法分析

    這篇文章主要介紹了Java?IO流之StringWriter和StringReader用法分析,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • MyBatis中映射文件的使用案例代碼

    MyBatis中映射文件的使用案例代碼

    這篇文章主要介紹了MyBatis中映射文件的使用,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • java有序二叉樹(shù)的刪除節(jié)點(diǎn)方式

    java有序二叉樹(shù)的刪除節(jié)點(diǎn)方式

    文章描述了在二叉樹(shù)中刪除節(jié)點(diǎn)的三種情況及其對(duì)應(yīng)的操作步驟,通過(guò)遞歸找到節(jié)點(diǎn)及其父節(jié)點(diǎn),并根據(jù)節(jié)點(diǎn)的子樹(shù)情況(無(wú)子樹(shù)、單子樹(shù)、雙子樹(shù))進(jìn)行相應(yīng)的刪除操作,文章還提供了一個(gè)測(cè)試類來(lái)驗(yàn)證刪除操作的正確性
    2024-12-12
  • SpringMVC實(shí)現(xiàn)自定義類型轉(zhuǎn)換器

    SpringMVC實(shí)現(xiàn)自定義類型轉(zhuǎn)換器

    本篇文章主要介紹了SpringMVC實(shí)現(xiàn)自定義類型轉(zhuǎn)換器 ,詳細(xì)的介紹了自定義類型轉(zhuǎn)換器的用法和好處,有興趣的可以了解一下。
    2017-04-04
  • Java多線程編程:線程的原理和使用安全

    Java多線程編程:線程的原理和使用安全

    文章主要介紹了線程的基本概念、線程和進(jìn)程的區(qū)別、線程的創(chuàng)建方法、線程的常見(jiàn)方法與屬性、線程的安全問(wèn)題等,線程是程序執(zhí)行流的最小單位,可以并發(fā)執(zhí)行并共享數(shù)據(jù),線程的生命周期狀態(tài)包括新建、就緒、運(yùn)行、阻塞、死亡等
    2026-05-05

最新評(píng)論

专栏| 浑源县| 碌曲县| 宜昌市| 隆子县| 剑川县| 钟山县| 策勒县| 逊克县| 科技| 淳安县| 宜兰县| 石家庄市| 韩城市| 凉城县| 隆林| 高邑县| 郓城县| 伊通| 霍山县| 安宁市| 景宁| 沧源| 望奎县| 黑河市| 新邵县| 洪雅县| 西吉县| 穆棱市| 方城县| 东山县| 肇源县| 北辰区| 子洲县| 蕉岭县| 固始县| 永康市| 灵武市| 大关县| 新田县| 泰顺县|