欧美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 語句表示如果當前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; //
            //當進入else的時候,就說明我們當前計算出來的Hash值在數(shù)組中的位置已經(jīng)存在了 ,那么就先進行判斷
            //如果當前索引位置對應(yīng)的鏈表的第一個元素的hash值和準備添加的key的hash值一樣
            //并且滿足 下面兩個條件之一:
            //(1) 準備加入的keyhash值 和 p 指向的Node 結(jié)點的hash值相同,那就說明是是同一個對象
            //(2)  當前的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() 對當前這個鏈表進行樹化(轉(zhuǎn)成紅黑樹)
                  //    注意,在轉(zhuǎn)成紅黑樹時,要進行判斷, 判斷條件
                  //    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
                  //            resize();
                  //    如果上面條件成立,先table擴容.
                  //    只有上面條件不成立時,才進行轉(zhuǎn)成紅黑樹
                  //(2) 依次和該鏈表的每一個元素比較過程中,如果有相同情況,就直接break
                //這是一個死循環(huán),會一直進行 比較,只有兩種情況,才會退出循環(huán)
                //第一種:當數(shù)組中的其中一條列表的長度到達了7,準備進行樹化的時候
                //第二種:就是發(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);
                        加入完一個元素之后,馬上的進行判斷,當前列表的個數(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練習

代碼演示:

在沒有重寫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)文章

最新評論