亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Set接口深入剖析之HashSet、LinkedHashSet和TreeSet

 更新時(shí)間:2023年09月18日 09:33:59   作者:努力的小鳴人  
這篇文章主要介紹了Set接口深入剖析之HashSet、LinkedHashSet和TreeSet,LinkedHashSet是HashSet的子類(lèi),實(shí)現(xiàn)了Set接口,LinkedHashSet底層是一個(gè)LinkedHashMap,底層維護(hù)了一個(gè)數(shù)組+雙向鏈表,需要的朋友可以參考下

一、基礎(chǔ)知識(shí)

  • Set接口:存儲(chǔ)無(wú)序、不可重復(fù)的數(shù)據(jù)
  • HashSet:主要實(shí)現(xiàn)類(lèi),線程不安全,可以存儲(chǔ)null值
  • LinkedHashSet:是HashSet的子類(lèi),遍歷內(nèi)部的數(shù)據(jù)時(shí),可以按照添加的順序遍歷
  • TreeSet:可以按照添加的對(duì)象指定屬性,進(jìn)行排序

二、HashSet

基于JDK 1.8 分析

定義

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable

HashSet繼承AbstractSet類(lèi),實(shí)現(xiàn)Set、Cloneable、Serializable接口

基本屬性

private transient HashMap<E,Object> map;
//定義一個(gè)Object對(duì)象作為HashMap的value
private static final Object PRESENT = new Object();

HashSet中數(shù)據(jù)是存放在HashMap中,HashSet基于HashMap,對(duì)數(shù)據(jù)的訪問(wèn)基本用HashMap的方法,另外存入Set中的數(shù)據(jù)本身是無(wú)序的,維護(hù)訪問(wèn)順序沒(méi)有意義

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

HashSet是可克隆對(duì)象和進(jìn)行序列化 ,其內(nèi)部的數(shù)據(jù)存儲(chǔ)區(qū)通過(guò)一個(gè)transient修飾的HashMap維護(hù),調(diào)用了HashMap的構(gòu)造函數(shù)完成。主要的參數(shù)是基礎(chǔ)容量為16個(gè)單位,加載因子是0.75

方法

public int size() {
        return map.size();
    }
public boolean isEmpty() {
        return map.isEmpty();
    }
public boolean contains(Object o) {
        return map.containsKey(o);
    }    
public boolean add(E e) {
        return map.put(e, PRESENT)==null;
	}
public V put(K key, V value) {
	    return putVal(hash(key), key, value, false, true);
	}
 /**
     * Returns an iterator over the elements in this set.  The elements
     * are returned in no particular order.
     *
     * @return an Iterator over the elements in this set
     * @see ConcurrentModificationException
     */
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }    

contains()判斷某個(gè)元素是否存在于HashSet()中,存在返回true,否則返回false,底層調(diào)用containsKey判斷HashMap的key值是否為空 底層調(diào)用HashMap的keySet返回所有的key,反映了HashSet中的所有元素都是保存在HashMap的key中,value則是使用的PRESENT對(duì)象,該對(duì)象為static final

public Object clone() {
        try {
            HashSet<E> newSet = (HashSet<E>) super.clone();
            newSet.map = (HashMap<E, Object>) map.clone();
            return newSet;
        } catch (CloneNotSupportedException e) {
            throw new InternalError();
        }
    }

clone返回此 HashSet 實(shí)例的淺表副本,并沒(méi)有復(fù)制這些元素

put方法

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    //確認(rèn)初始化
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //如果桶為空,直接插入新元素,也就是entry
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        //如果沖突,分為三種情況
        //key相等時(shí)讓舊entry等于新entry即可
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //紅黑樹(shù)情況
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            //如果key不相等,則連成鏈表
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                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;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

hashset不允許重復(fù)的元素加入,因?yàn)橹灰猭ey的equals方法判斷為true時(shí)會(huì)發(fā)生value的替換,即當(dāng)兩個(gè)hashcode相同但key不相等的entry插入時(shí),仍然會(huì)連成一個(gè)鏈表,長(zhǎng)度超過(guò)8時(shí)依然會(huì)和hashmap一樣擴(kuò)展成紅黑樹(shù),當(dāng)add方法發(fā)生沖突時(shí),如果key相同,則替換value,如果key不同,則連成鏈表

小結(jié)

  1. HashSet有以下特點(diǎn)
    • 不能保證元素的排列順序,順序有可能發(fā)生變化
    • 不是同步的
    • 集合元素可以是null,但只能放入一個(gè)null
  2. 當(dāng)向HashSet結(jié)合中存入一個(gè)元素時(shí),HashSet會(huì)調(diào)用該對(duì)象的hashCode()方法來(lái)得到該對(duì)象的hashCode值,然后根據(jù) hashCode值來(lái)決定該對(duì)象在HashSet中存儲(chǔ)位置
  3. HashSet集合判斷兩個(gè)元素相等的標(biāo)準(zhǔn)是兩個(gè)對(duì)象通過(guò)equals方法比較相等,并且兩個(gè)對(duì)象的hashCode()方法返回值相等

注:如果要把一個(gè)對(duì)象放入HashSet中,重寫(xiě)該對(duì)象對(duì)應(yīng)類(lèi)equals方法,也應(yīng)該重寫(xiě)其hashCode()方法:其規(guī)則是如果兩個(gè)對(duì)象通過(guò)equals方法比較返回true時(shí),其hashCode也應(yīng)該相同。另對(duì)象中用作equals比較標(biāo)準(zhǔn)的屬性,都應(yīng)用來(lái)計(jì)算 hashCode的值

三、LinkedHashSet

LinkedHashSet集合同樣是根據(jù)元素的hashCode值來(lái)決定元素的存儲(chǔ)位置,但是它同時(shí)使用鏈表維護(hù)元素的次序。這樣使得元素看起 來(lái)像是以插入順序保存的,當(dāng)遍歷該集合時(shí)候,LinkedHashSet將會(huì)以元素的添加順序訪問(wèn)集合的元素

構(gòu)造函數(shù)

//Constructor - 1
public LinkedHashSet(int initialCapacity, float loadFactor)
{
      super(initialCapacity, loadFactor, true);              //Calling super class constructor
}
//Constructor - 2
public LinkedHashSet(int initialCapacity)
{
        super(initialCapacity, .75f, true);             //Calling super class constructor
}
//Constructor - 3
public LinkedHashSet()
{
        super(16, .75f, true);                //Calling super class constructor
}
//Constructor - 4
public LinkedHashSet(Collection<? extends E> c)
{
        super(Math.max(2*c.size(), 11), .75f, true);          //Calling super class constructor
        addAll(c);
}

四個(gè)構(gòu)造函數(shù)調(diào)用的是同一個(gè)父類(lèi)構(gòu)造函數(shù),該構(gòu)造函數(shù)是一個(gè)包內(nèi)私有構(gòu)造函數(shù),它只能被LinkedHashSet使用,該構(gòu)造函數(shù)需要初始容量,負(fù)載因子和 boolean類(lèi)型的啞值等參數(shù),這個(gè)啞參數(shù)只是用來(lái)區(qū)別這個(gè)構(gòu)造函數(shù)與HashSet的其他擁有初始容量和負(fù)載因子參數(shù)的構(gòu)造函數(shù)

注:?jiǎn)≈担簺](méi)有什么用處的參數(shù),作為標(biāo)記

小結(jié)

LinkedHashSet直接繼承自HashSet,能夠維護(hù)基礎(chǔ)的有序性,LinkedHashSet并沒(méi)有自己的方法,所有方法都繼承自它的父類(lèi)HashSet,因此對(duì)LinkedHashSet的所有操作方式就像對(duì)HashSet操作一樣,唯一的不同是內(nèi)部使用不同的對(duì)象去存儲(chǔ)元素。在HashSet中,插入的元素是被當(dāng)做HashMap的鍵來(lái)保存的,而在LinkedHashSet中被看作是LinkedHashMap的鍵

換句話說(shuō) LinkedHashSet繼承自HashSet,源碼更少、更簡(jiǎn)單,唯一的區(qū)別是LinkedHashSet內(nèi)部使用的是LinkHashMap

特點(diǎn):

  • 有序,按照插入順序
  • 不是同步的
  • 集合元素可以是null,但只能放入一個(gè)null
  • 沒(méi)有set和get方法 只能通過(guò)迭代器取值

LinkedHashSet在迭代訪問(wèn)Set中的全部元素時(shí),性能比HashSet好,但是插入時(shí)性能稍微遜色于HashSet

四、TreeSet

定義

TreeSet與TreeMap實(shí)現(xiàn)類(lèi)似

TreeMap是一個(gè)有序的二叉樹(shù),TreeSet同樣也是一個(gè)有序的二叉樹(shù),它的作用是提供有序的Set集合。TreeSet繼承自AbstractSet,實(shí)現(xiàn)了Set接口、Cloneable和Serializable、NavigableSet接口。其內(nèi)部主要是通過(guò)一個(gè)NavigableMap的map維護(hù)數(shù)據(jù)存儲(chǔ)

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable

構(gòu)造方法

//默認(rèn)構(gòu)造方法
public TreeSet() {
    this(new TreeMap<E,Object>());
}
//構(gòu)造包含指定 collection 元素的新 TreeSet,按照其元素的自然順序進(jìn)行排序。
public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
}
//構(gòu)造新的空 TreeSet,根據(jù)指定比較器進(jìn)行排序。
public TreeSet(Collection<? extends E> c) {
    this();
    addAll(c);
}
//構(gòu)造與指定有序 set 具有相同映射關(guān)系和相同排序的新 TreeSet
public TreeSet(SortedSet<E> s) {
    this(s.comparator());
    addAll(s);
}
TreeSet(NavigableMap<E,Object> m) {
    this.m = m;
}

主要方法

1.add:將指定的元素添加到此 set

public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }
public V put(K key, V value) {
    Entry<K,V> t = root;
    if (t == null) {
    //空樹(shù)時(shí),判斷節(jié)點(diǎn)是否為空
        compare(key, key); // type (and possibly null) check
        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    int cmp;
    Entry<K,V> parent;
    // split comparator and comparable paths
    Comparator<? super K> cpr = comparator;
    //非空樹(shù),根據(jù)傳入比較器進(jìn)行節(jié)點(diǎn)的插入位置查找
    if (cpr != null) {
        do {
            parent = t;
            //節(jié)點(diǎn)比根節(jié)點(diǎn)小,則找左子樹(shù),否則找右子樹(shù)
            cmp = cpr.compare(key, t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
                //如果key的比較返回值相等,直接更新值(一般compareto相等時(shí)equals方法也相等)
            else
                return t.setValue(value);
        } while (t != null);
    }
    else {
    //如果沒(méi)有傳入比較器,則按照自然排序
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
        do {
            parent = t;
            cmp = k.compareTo(t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                return t.setValue(value);
        } while (t != null);
    }
    //查找的節(jié)點(diǎn)為空,直接插入,默認(rèn)為紅節(jié)點(diǎn)
    Entry<K,V> e = new Entry<>(key, value, parent);
    if (cmp < 0)
        parent.left = e;
    else
        parent.right = e;
        //插入后進(jìn)行紅黑樹(shù)調(diào)整
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
}    

get:獲取元素

public V get(Object key) {
    Entry<K,V> p = getEntry(key);
    return (p==null ? null : p.value);
}

與put的流程類(lèi)似,只把插入換成了查找

ceiling:返回此 set 中大于等于給定元素的最小元素;如果不存在這樣的元素,則返回 null

太多了直接上腦圖:

在這里插入圖片描述

自然排序與定制排序

TreeSet支持兩種排序方式,自然排序 和 定制排序

小結(jié)

  1. TreeSet內(nèi)部是通過(guò)TreeMap構(gòu)造出來(lái)的
    • 有序,按照比較器排序,并非按照插入順序
    • 不是同步的
    • 集合元素可以是null,但只能放入一個(gè)null
    • 沒(méi)有set和get方法 只能通過(guò)迭代器取值
  2. TreeSet是SortedSet接口的唯一實(shí)現(xiàn)類(lèi),TreeSet可以確保集合元素處于排序狀態(tài)

總結(jié):HashSet、LinkedHashSet和TreeSet,三者其內(nèi)部都是基于Map來(lái)實(shí)現(xiàn)的。TreeSet和LinkedHashSet分別使用了TreeMap和LinkedHashMap來(lái)控制訪問(wèn)數(shù)據(jù)的有序性。都屬于Set的范疇,都是沒(méi)有重復(fù)元素的集合。基于HashMap和TreeMap,也都是非線程安全的

到此這篇關(guān)于Set接口深入剖析之HashSet、LinkedHashSet和TreeSet的文章就介紹到這了,更多相關(guān)Set接口深入剖析內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java利用Dijkstra算法求解拓?fù)潢P(guān)系最短路徑

    Java利用Dijkstra算法求解拓?fù)潢P(guān)系最短路徑

    迪杰斯特拉算法(Dijkstra)是由荷蘭計(jì)算機(jī)科學(xué)迪家迪杰斯特拉于1959年提出的,因此又叫狄克斯特拉算法。本文將利用迪克斯特拉(Dijkstra)算法求拓?fù)潢P(guān)系最短路徑,感興趣的可以了解一下
    2022-07-07
  • 詳解spring boot mybatis全注解化

    詳解spring boot mybatis全注解化

    這篇文章主要介紹了spring boot mybatis全注解化的相關(guān)資料,需要的朋友可以參考下
    2017-09-09
  • 解決spring @ControllerAdvice處理異常無(wú)法正確匹配自定義異常

    解決spring @ControllerAdvice處理異常無(wú)法正確匹配自定義異常

    這篇文章主要介紹了解決spring @ControllerAdvice處理異常無(wú)法正確匹配自定義異常的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Java遍歷起止日期中間的所有日期操作

    Java遍歷起止日期中間的所有日期操作

    這篇文章主要介紹了Java遍歷起止日期中間的所有日期操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • Java?HashTable與Collections.synchronizedMap源碼深入解析

    Java?HashTable與Collections.synchronizedMap源碼深入解析

    HashTable是jdk?1.0中引入的產(chǎn)物,基本上現(xiàn)在很少使用了,但是會(huì)在面試中經(jīng)常被問(wèn)到。本文就來(lái)帶大家一起深入了解一下Hashtable,需要的可以參考一下
    2022-11-11
  • 教大家使用java實(shí)現(xiàn)頂一下踩一下功能

    教大家使用java實(shí)現(xiàn)頂一下踩一下功能

    這篇文章主要教大家如何使用java實(shí)現(xiàn)頂一下踩一下功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • Java中將List列表轉(zhuǎn)換為字符串的三種方法

    Java中將List列表轉(zhuǎn)換為字符串的三種方法

    這篇文章主要介紹了如何在 Java中將List 轉(zhuǎn)換為 String,接下來(lái)使用Java 8 Streams Collectors api和String.join()方法將帶有逗號(hào)分隔符或自定義分隔符的集合轉(zhuǎn)換為字符串,需要的朋友可以參考下
    2025-04-04
  • java獲取用戶輸入的字符串方法

    java獲取用戶輸入的字符串方法

    今天小編就為大家分享一篇java獲取用戶輸入的字符串方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • java 解壓與壓縮文件夾的實(shí)例詳解

    java 解壓與壓縮文件夾的實(shí)例詳解

    這篇文章主要介紹了 java 解壓與壓縮文件夾的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文能幫助到大家,讓大家實(shí)現(xiàn)這樣的功能,掌握這樣的方法,需要的朋友可以參考下
    2017-10-10
  • Mybatis-Plus自動(dòng)生成代碼的實(shí)現(xiàn)示例

    Mybatis-Plus自動(dòng)生成代碼的實(shí)現(xiàn)示例

    在工作中,程序員很多時(shí)候都是在寫(xiě)類(lèi)似的代碼,可以使用自動(dòng)生成代碼,本文主要介紹了Mybatis-Plus自動(dòng)生成代碼的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11

最新評(píng)論