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

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 的基礎上維護了元素添加順序的功能。

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

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

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

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

LinkedHashSet源碼分析

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

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

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

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

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

源碼分析

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

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

/*
對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對應的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 語句表示如果當前table 是null, 或者 大小=0
        //就會進行第一次擴容,到16個空間.
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //(1)根據(jù)key,得到hash 去計算該key應該存放到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; //
            //當進入else的時候,就說明我們當前計算出來的Hash值在數(shù)組中的位置已經存在了 ,那么就先進行判斷
            //如果當前索引位置對應的鏈表的第一個元素的hash值和準備添加的key的hash值一樣
            //并且滿足 下面兩個條件之一:
            //(1) 準備加入的keyhash值 和 p 指向的Node 結點的hash值相同,那就說明是是同一個對象
            //(2)  當前的key對象 或者和我們傳入對象的地址相同,因為==在判斷引用類型的時候,判斷的是地址是否相同,如果地址相同,或者他們的內容相同
            //就不能加入     如果不能加入就把p賦給e
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //再判斷 p 是不是一顆紅黑樹,
            //如果是一顆紅黑樹,就調用 putTreeVal , 來進行添加
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                //如果上面的情況都不是的話,那么就說明,此時這個索引對應的位置是一個鏈表了
            else {//如果table對應索引位置,已經是一個鏈表, 就使用for循環(huán)比較
                  //(1) 依次和該鏈表的每一個元素比較后,都不相同, 則加入到該鏈表的最后
                  //    注意在把元素添加到鏈表后,立即判斷 該鏈表是否已經達到8個結點
                  //    , 就調用 treeifyBin() 對當前這個鏈表進行樹化(轉成紅黑樹)
                  //    注意,在轉成紅黑樹時,要進行判斷, 判斷條件
                  //    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
                  //            resize();
                  //    如果上面條件成立,先table擴容.
                  //    只有上面條件不成立時,才進行轉成紅黑樹
                  //(2) 依次和該鏈表的每一個元素比較過程中,如果有相同情況,就直接break
                //這是一個死循環(huán),會一直進行 比較,只有兩種情況,才會退出循環(huán)
                //第一種:當數(shù)組中的其中一條列表的長度到達了7,準備進行樹化的時候
                //第二種:就是發(fā)現(xiàn)我們加入的元素,在這個列表中發(fā)現(xiàn)了重復的,也會直接跳出循環(huán)
                for (int binCount = 0; ; ++binCount) {
                        這里e=p.next 因為我們在最開始上面的時候,已經對第一個元素進行了判斷,所以這里直接從下一個元素開始判斷
                        如果下一個元素為空,那么就直接加入
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        加入完一個元素之后,馬上的進行判斷,當前列表的個數(shù)有幾個,是否進行樹化
                        if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    //這里就是,在循環(huán)比較的過程中,如果發(fā)現(xiàn)有相同的內容,那么會直接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 就是我們每加入一個結點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 底層結構 (數(shù)組table+雙向鏈表)
        //4. 添加第一次時,直接將 數(shù)組table 擴容到 16 ,存放的結點類型是 LinkedHashMap$Entry
        //5. 數(shù)組是 HashMap$Node[] 存放的元素/數(shù)據(jù)是 LinkedHashMap$Entry類型
        /*
                //繼承關系是在內部類完成.
                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練習

代碼演示:

在沒有重寫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 +
                '}';
    }
}

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

相關文章

  • 基于Nacos實現(xiàn)SpringBoot動態(tài)定時任務調度

    基于Nacos實現(xiàn)SpringBoot動態(tài)定時任務調度

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

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

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

    Java SpringMVC的@RequestMapping注解使用及說明

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

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

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

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

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

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

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

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

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

    java有序二叉樹的刪除節(jié)點方式

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

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

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

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

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

最新評論

六盘水市| 西乡县| 黄石市| 三穗县| 平顶山市| 满城县| 五原县| 贵定县| 沅陵县| 甘肃省| 阿克陶县| 宜丰县| 尼玛县| 盐边县| 巫山县| 鹤岗市| 获嘉县| 乌兰察布市| 嘉兴市| 郎溪县| 乐平市| 吴忠市| 大竹县| 八宿县| 云浮市| 平舆县| 庐江县| 平湖市| 五台县| 衡水市| 龙山县| 洪洞县| 和平县| 宝鸡市| 禄劝| 报价| 山丹县| 成安县| 临泽县| 繁昌县| 无极县|