主頁 > 後端開發 > Java集合框架之Set,Map

Java集合框架之Set,Map

2021-12-21 08:03:40 後端開發

大家好,我是Morning,在CSDN寫文,分享一些Java基礎知識,一些自己認為在學習程序中比較重要的東西,致力于幫助初學者入門,希望可以幫助你進步,感興趣的歡迎關注博主,和博主一起學習Java知識,大家還可以去專欄查看之前的文章,希望未來能和大家共同探討技術,

文章目錄

      • Set介面
        • HashSet
          • 散串列
        • TreeSet
      • Map介面
        • HashMap
          • 負載因子
        • TreeMap

書接上回,這 一篇,一起學習Set,Map介面,和它的實作類,

Set介面

set介面等同于Collection介面,不過其方法的行為有更嚴謹的定義,set的add方法不允許增加重復的元素,要適當地定義set的equals方法:只要倆個set包含同樣的元素就認為它們是相同的,而不要求這些元素有相同的順序,hashCode方法的定義要保證包含相同元素的倆個set會得到相同的散列碼,
——Java核心技術 卷一

public interface Set<E> extends Collection<E> {
    //一些方法
}

set不允許包含相同的元素,如果呼叫 add 方法來添加倆個相同的元素,那么在添加第二個元素時就會回傳 false,這個介面有倆個比較常用的實作類:HashSet,TreeSet,HashSet是一個沒有重復元素的一個無序集合,而TreeSet是一個有序集在這里插入圖片描述

HashSet

HashSet 使用陣列和鏈表來實作散串列,不同 hashcode 的值存放在不同陣列下標的位置,相同 hashcode 的值存放在相同陣列下標的鏈表上的不同位置上,

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable{
    //一些方法
}

HashSet類中有四個構造方法:

public HashSet()   //構造一個空的散列集
public HashSet(Collection<? extends E> c) //構造一個散列集,并將集合中的所有元素添加到這個散列集中
public HashSet(int initialCapacity, float loadFactor) //構造一個有指定容量和裝填因子的空散列集
public HashSet(int initialCapacity)  //構造一個指定容量的空散列集

現在讓我們來看一下add方法的原始碼:

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

可以看到這里面是呼叫了 map.put() 方法,map 在HashSet中的宣告如下:

private transient HashMap<E,Object> map;

由此可見HashSet和HashMap有很緊密的聯系,

HashSet的底層是用一個HashMap來實作的,HashSet再添加時如何擴容問題在下文HashMap中再詳細介紹,

散串列

Java中,散串列用鏈表,陣列實作,也就可以這么理解,在陣列不同索引位置上放的是一個鏈表,要想查找表中物件的位置,要先計算它的散列碼,然后與陣列長度取余,所得到的結果就是保存這個元素的索引,在保存物件時,通過這個計算方式得到了索引位置,如果這個位置沒有其他元素,那就將元素直接插入到該位置就好了,如果這個位置已經有其他元素了,那么需要將這個新的物件與已有的物件進行比較,查看這個物件是否已經存在,如果不存在就在鏈表的末尾存盤這個物件,

TreeSet

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable{
    //一些方法
}

樹集是一個有序集合,可以以任意順序將元素插入集合中,

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-LmJuZcAV-1639916281542)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1639904244362.png)]

TreeSet排序是用一個紅黑樹完成的,每次將一個元素添加到樹中時,都會將其放置到正確的排序位置上,由于樹集時有序的,所以將一個元素添加到樹中要比添加到散串列中慢,所以如果不需要資料是有序的,就沒必要為了排序而影響性能,

它也有四個建構式:

public TreeSet()  //構造一個空的樹集,排序方式是自然排序
public TreeSet(Comparator<? super E> comparator)  //構造一個空樹集,指定比較器,就是排序方式
public TreeSet(Collection<? extends E> c)   //構造一個樹集,并將集合中的所有元素添加到這個樹集中
public TreeSet(SortedSet<E> s)     //構造一個樹集,并增加一個集合或者有序集中的所有元素,如果是有序集的話,要使用同樣的順序,

上面提到的自然排序是用元素的 compareTo() 方法來比較元素的大小關系(比如你存入v,b,a,就會輸出a,b,v),然后將集合元素按照升序排列,

Map介面

集是一個集合,允許你快速的查找現有的元素,但是要查找一個元素,需要有所要查找的那個元素的準確副本,這不是一種常見的查找方式,通常,我們知道某些關鍵資訊,希望查找與之關聯的元素,映射(map)資料結構就是為此設計的,映射用來存放鍵 / 值對,如果提供了鍵,就可以查找到值,
——Java核心技術

public interface Map<K,V> {
	int size();  //回傳映射中的元素數
    V get(Object key);  //回傳指定鍵對應的值
    V remove(Object key); //從映射中洗掉指定鍵對應的元素,
}

Java為映射提供了倆個通用的實作:HashMap和TreeMap

HashMap

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    
}

實作了Map介面,存盤的內容是鍵值對(key-value)映射,將鍵進行散列,散列或比較函式只應用于鍵,與鍵相關的值不進行散列或比較,

HashMap底層的結構是,散串列(陣列和鏈表)和紅黑樹

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oi9ZErOs-1639916281543)(C:\Users\DELL\AppData\Roaming\Typora\typora-user-images\1639910918514.png)]

我用反射的方式輸出了這個map的容量,可以看到如果使用無參的構造方法,那么map的容量默認為16,我們也可以通過傳參的方式,來指定它的容量,值的一提的是,你指定的容量,一定要為 2 的冪次方,且要小于 int 范圍內最大的 2 的冪次方數(1073741824),

//指定容量
HashMap<String,String> map = new HashMap<String, String>(8);

這個構造方法還是呼叫了下面的這個構造方法,只是把默認的負載因子值傳進去了:

public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
//呼叫這個構造方法
public HashMap(int initialCapacity, float loadFactor) {
    //判斷是否小于0,拋出例外
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal initial capacity: " +
                                           initialCapacity);
    //判斷是否大于最大值
    if (initialCapacity > MAXIMUM_CAPACITY)
        initialCapacity = MAXIMUM_CAPACITY;
    //判斷負載因子的合法性
    if (loadFactor <= 0 || Float.isNaN(loadFactor))
        throw new IllegalArgumentException("Illegal load factor: " +
                                           loadFactor);
    this.loadFactor = loadFactor;  //更新負載因子的值
    //判斷需要擴容的臨界值
    this.threshold = tableSizeFor(initialCapacity);
}

put方法,直接看原始碼:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

呼叫put方法后,會根據鍵值來計算一個值(位置),然后呼叫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;
    //hash表為慷訓者長度為0的話,創建hash表
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;//resize()HashMap擴容的方法
    if ((p = tab[i = (n - 1) & hash]) == null)
        //如果位置上沒有元素,就加進去,成為鏈表的頭結點
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        //如果這個值的型別為樹結構的話,就直接添加到樹中,不會判斷是否超過8
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            //binCount鏈表的長度
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    //如果鏈表不為空,就往下一個節點添加
                    p.next = newNode(hash, key, value, null);
                    //如果這個鏈表的長度大于8,就會轉為紅黑樹存盤
                    if (binCount >= TREEIFY_THRESHOLD - 1) 
                        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;
    //如果里面的元素超過 threshold 的話就擴容
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

通過代碼中的注釋,相信你已經初步了解了 put 方法,總的來說就是添加時首先是用 k 通過哈希函式計算位置,位置上如果沒有元素添加在鏈表的頭結點,如果有插入到鏈表的下一個節點,當鏈表的長度為大于等于8時,轉為紅黑樹存盤,如果該鍵已經有映射關系的話,用這次的值覆寫掉之前的值,最后判斷要不要擴容,如果要擴容,會擴容為原來容量的2倍,這樣效率會高一些,也可以減少哈希沖突,

負載因子

上文中我們提到了負載因子,那么負載因子是什么呢?

負載因子也叫裝填因子,是一個0.0~1.0之間的數,確定散串列填充的百分比,當大于這個百分比時,散串列進行再散列,在map中,也就是說,當put的元素數量超過一定值的時候,就會擴容,這個值就是負載因子*容量,

HashMap中的負載因子默認是0.75:

static final float DEFAULT_LOAD_FACTOR = 0.75f;

就是說如果map中的元素超過容量的3/4就要進行擴容,

那么為什么這里要默認為0.75呢?這了也是規定了一個相對合理的值,如果為 1 的話效率太低,滿了之后才擴容;如果為0.5的話,浪費空間,所以選了一個居中的值,

TreeMap

public class TreeMap<K,V>
    extends AbstractMap<K,V>
    implements NavigableMap<K,V>, Cloneable, java.io.Serializable{
    
}

它沒有實作Map介面是因為AbstractMap實作了Map介面,TreeMap是一個能比較元素大小的Map集合,可以對 key 值進行大小排序,其中,可以使用自然順序,也可以使用集合中自定義的比較器來進行排序,底層是紅黑樹結構,

TreeMap中運用到的紅黑樹結構,是資料結構的一種,相關知識太多,大家感興趣的話可以在網上查閱資料(博主對樹結構的理解不是很透徹😢😢),

好了,本次的分享到這里就結束了,博主會在日后給大家分享其他的知識,和大家一起探討,有興趣的可以關注博主,文中有什么不當的地方,歡迎大家在評論區指出,大家一起探討、學習,🤞🤞🤞

參考書籍:Java核心技術 卷一(第11版)

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/387882.html

標籤:java

上一篇:做Android開發怎么才能不被淘汰?,kotlin語言就是你最好的選擇

下一篇:一個月內面了30家企業,不斷對比薪資,我從18K變成了38K

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more