Java中的HashSet、LinkedHashSet集合解析
Set系列集合
- List系列集合:添加的元素是有序、可重復(fù)、有索引
- Set系列集合:添加的元素是無(wú)序、不可重復(fù)、無(wú)索引
- 無(wú)序:存取順序不一致
- 不重復(fù):可以去除重復(fù)
- 無(wú)索引:沒有帶索引的方法,所以不能使用普通for循環(huán)遍歷,也不能通過(guò)索引來(lái)獲取元素
Set集合的實(shí)現(xiàn)類
- HashSet:無(wú)序、不重復(fù)、無(wú)索引
- LinkedHashSet:有序、不重復(fù)、無(wú)索引
- TreeSet:可排序、不重復(fù)、無(wú)索引
Set接口中的方法上基本上與Collection的API一致
Collection
Collection是單列集合的祖宗接口,它的功能是全部單列集合都可以繼承使用的
| 方法名稱 | 說(shuō)明 |
| public boolean add(E e) | 把給定的對(duì)象添加到當(dāng)前集合中 |
| public void clear() | 清空集合中所有的元素 |
| public boolean remove(E e) | 把給定的對(duì)象在當(dāng)前集合中刪除 |
| public boolean contains(Object obj) | 判斷當(dāng)前集合中是否包含給定的對(duì)象 |
| public boolean isEmpty() | 判斷當(dāng)前集合是否為空 |
| public int size() | 返回集合中元素的個(gè)數(shù)/集合長(zhǎng)度 |
練習(xí):存儲(chǔ)字符串并遍歷
利用Set系列的集合,添加字符串,并使用多種方式遍歷
1。迭代器
2。增強(qiáng)for
3。Lambda表達(dá)式
public class A01_SetDemo1 {
public static void main(String[] args) {
//1.創(chuàng)建一個(gè)Set集合的對(duì)象
Set<String> s = new HashSet<>();
//2.添加元素
boolean r1 = s.add("朵朵");
boolean r2 = s.add("朵朵");
//true
System.out.println(r1);
//false
System.out.println(r2);
//[朵朵]
System.out.println(s);
boolean r3 = s.add("小七");
boolean r4 = s.add("鋼镚");
//無(wú)序
//[鋼镚, 朵朵, 小七]
System.out.println(s);
//迭代器遍歷
Iterator<String> it = s.iterator();
while (it.hasNext()) {
String str = it.next();
System.out.println(str);
}
//增強(qiáng)for
for (String str : s) {
System.out.println(str);
}
//Lambda
s.forEach(str -> System.out.println(str));
}
}HashSet
- HashSet集合底層采用哈希表存儲(chǔ)數(shù)據(jù)
- 哈希表是一種對(duì)與增刪改查數(shù)據(jù)性能都較好的結(jié)構(gòu)
哈希表組成
- JDK8之前:數(shù)組+鏈表
- JDK8之后:數(shù)組+鏈表+紅黑樹
哈希值
對(duì)象的整數(shù)表現(xiàn)形式
- 根據(jù)hashCode方法算出來(lái)的int類型的整數(shù)
- 該方法定義在Object類中,所有對(duì)象都可以調(diào)用,默認(rèn)使用地址值進(jìn)行計(jì)算
- 一般情況下,會(huì)重寫hashCode方法,利用對(duì)象內(nèi)部的屬性值計(jì)算哈希值
對(duì)象的哈希值特點(diǎn)
- 如果沒有重寫hashCode方法,不同對(duì)象計(jì)算出的哈希值是不同的
- 如果已經(jīng)重寫hashCode方法,不同的對(duì)象只要屬性值相同,計(jì)算出的哈希值就是一樣的
- 在小部分情況下,不同的屬性值或者不同地址值計(jì)算出來(lái)的哈希值也可能一樣(哈希碰撞)
public class A02_HashSetDemo1 {
public static void main(String[] args) {
//1。創(chuàng)建對(duì)象
Student s1 = new Student("朵朵", 5);
Student s2 = new Student("朵朵", 5);
//2。 如果沒有重寫hashCode方法,不同對(duì)象計(jì)算出的哈希值是不同的
//1554874502
System.out.println(s1.hashCode());
//1846274136
System.out.println(s2.hashCode());
//重寫hashCode后:不同的對(duì)象只要屬性值相同,計(jì)算出的哈希值就是一樣的
//26209637
System.out.println(s1.hashCode());
//26209637
System.out.println(s2.hashCode());
//3。在小部分情況下,不同的屬性值或者不同地址值計(jì)算出來(lái)的哈希值也可能一樣(哈希碰撞)
//96354
System.out.println("abc".hashCode());
//96354
System.out.println("acD".hashCode());
}
}HashSet的底層原理

1.創(chuàng)建一個(gè)默認(rèn)長(zhǎng)度16,默認(rèn)加載因子0.75的數(shù)組,數(shù)組名table
- 加載因子:擴(kuò)容時(shí)機(jī)
- 當(dāng)存入數(shù)據(jù)達(dá)到16 x 0.75 = 12時(shí)候,數(shù)組擴(kuò)容成原數(shù)組的二倍
- JKD8以后:當(dāng)鏈表長(zhǎng)度大于8而且數(shù)組長(zhǎng)度大于等于64,當(dāng)前鏈表自動(dòng)轉(zhuǎn)成紅黑樹,增加查找效率
2.根據(jù)元素的哈希值跟數(shù)組的長(zhǎng)度計(jì)算出應(yīng)存入的位置
- int index = (數(shù)組長(zhǎng)度 - 1) & 哈希值;
- 如果集合中存儲(chǔ)的是自定義對(duì)象,必須要重寫hashCode和equsl方法
3.判斷當(dāng)前位置是否為null,如果是null直接存入(添加元素)
4.如果位置不是null,表示有元素,則調(diào)用equals方法比較屬性值
- 一樣:不存 ------ 不一樣:存入數(shù)組,形成鏈表
- JDK8以前:新元素存入數(shù)組,老元素掛在新元素下面
- JDK8以后:新元素掛在老元素下面
HashSet的三個(gè)問(wèn)題
- 問(wèn)題1.HashSet為什么存和取的順序不一樣?
- HashSet遍歷是從數(shù)組的0索引開始,一條鏈表一條鏈表(或紅黑樹)的遍歷,可能和存儲(chǔ)時(shí)候的順序不一樣
- 問(wèn)題2.HashSet為什么沒有索引?
- HashSet不夠純粹,在底層是鏈表 數(shù)組 紅黑樹三種組合,定義誰(shuí)都不合適。
- 假設(shè)使用數(shù)組的索引,但一個(gè)索引處可能會(huì)有鏈表,不可能鏈表上所有的數(shù)據(jù)都是一個(gè)索引
- 問(wèn)題3.HashSet是利用什么機(jī)制保證數(shù)據(jù)去重復(fù)的
- HashCode方法 --> 哈希值 --> 確定元素添加在哪個(gè)位置
- equals方法 --> 比較內(nèi)部的屬性值是否相同(自定義對(duì)象要重寫這兩種方法)
練習(xí):利用HashSet集合去除重復(fù)元素
需求:創(chuàng)建一個(gè)存儲(chǔ)學(xué)生對(duì)象的集合,存儲(chǔ)多個(gè)學(xué)生對(duì)象 使用程序?qū)崿F(xiàn)在控制臺(tái)遍歷該集合
要求:學(xué)生對(duì)象的成員變量值相同,我們就認(rèn)為是同一個(gè)對(duì)象
public class A02_HashSetDemo2 {
public static void main(String[] args) {
//1。創(chuàng)建3個(gè)學(xué)生對(duì)象
Student s1 = new Student("朵朵", 5);
Student s2 = new Student("小七", 7);
Student s3 = new Student("鋼镚", 9);
Student s4 = new Student("朵朵", 5);
//2。創(chuàng)建集合用來(lái)添加學(xué)生
HashSet<Student> hs = new HashSet<>();
//3。添加元素(重寫hashCode和equal方法后)
//true
System.out.println(hs.add(s1));
//true
System.out.println(hs.add(s2));
//true
System.out.println(hs.add(s3));
//false
System.out.println(hs.add(s4));
//4。打印集合
//[Student{name = 小七, age = 7}, Student{name = 鋼镚, age = 9}, Student{name = 朵朵, age = 5}]
System.out.println(hs);
}
}LinkedHashSet
LinkedHashSet底層原理
- 有序、不重復(fù)、無(wú)索引
- 這里的有序指的是保證存儲(chǔ)和取出的元素順序一致
- 原理:底層數(shù)據(jù)結(jié)構(gòu)依然是哈希表,只是每個(gè)元素又額外多了一個(gè)雙鏈表的機(jī)制記錄存儲(chǔ)的順序
public class A04_LinkedHashSetDemo {
public static void main(String[] args) {
//1。創(chuàng)建3個(gè)學(xué)生對(duì)象
Student s1 = new Student("朵朵", 5);
Student s2 = new Student("小七", 7);
Student s3 = new Student("鋼镚", 9);
Student s4 = new Student("朵朵", 5);
//2。創(chuàng)建集合用來(lái)添加學(xué)生
LinkedHashSet<Student> lhs = new LinkedHashSet<>();
//3。添加元素(重寫hashCode和equal方法后)
//true
System.out.println(lhs.add(s1));
//true
System.out.println(lhs.add(s2));
//true
System.out.println(lhs.add(s3));
//false
System.out.println(lhs.add(s4));
//4。打印集合
//與添加順序一樣
//[Student{name = 朵朵, age = 5}, Student{name = 小七, age = 7}, Student{name = 鋼镚, age = 9}]
System.out.println(lhs);
}
}
數(shù)據(jù)去重
- 默認(rèn)使用HashSet
- 如果要求去重且存取有序,才使用LinkedHashSet
到此這篇關(guān)于Java中的HashSet、LinkedHashSet集合解析的文章就介紹到這了,更多相關(guān)Java中的HashSet、LinkedHashSet內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
iOS socket網(wǎng)絡(luò)編程實(shí)例詳解
socket是一個(gè)針對(duì)TCP和UDP編程的接口,你可以借助它建立TCP連接等。這篇文章主要介紹了iOS socket網(wǎng)絡(luò)編程 ,需要的朋友可以參考下2017-03-03
使用java項(xiàng)目搭建一個(gè)netty服務(wù)
這篇文章主要為大家詳細(xì)介紹了如何使用java項(xiàng)目搭建一個(gè)netty服務(wù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-10-10
Spring Boot中Elasticsearch的連接配置原理與使用詳解
在Spring Boot中,我們可以通過(guò)Elasticsearch實(shí)現(xiàn)對(duì)數(shù)據(jù)的搜索和分析,本文將介紹Spring Boot中Elasticsearch的連接配置、原理和使用方法,感興趣的可以了解一下2023-09-09
Mybatis中使用updateBatch進(jìn)行批量更新
這篇文章主要介紹了Mybatis中使用updateBatch進(jìn)行批量更新的相關(guān)資料,有逐條更新,sql批量更新等,具體實(shí)例代碼大家參考下本文2018-04-04
Java網(wǎng)約車項(xiàng)目實(shí)戰(zhàn)之實(shí)現(xiàn)搶單功能詳解
本文詳細(xì)介紹了如何使用Java實(shí)現(xiàn)網(wǎng)約車項(xiàng)目的搶單功能,并提供了一個(gè)完整的代碼示例,以便讀者能夠直接運(yùn)行和參考,感興趣的朋友一起看看吧2024-12-12
Java中this關(guān)鍵字的4種用法詳解(附常見面試題)
在Java的編程世界里,this關(guān)鍵字宛如一把神奇的鑰匙,看似簡(jiǎn)單,卻蘊(yùn)含著強(qiáng)大的功能,這篇文章主要介紹了Java中this關(guān)鍵字的4種用法的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-06-06
SpringBoot實(shí)現(xiàn)登錄攔截的示例代碼
如果我們不進(jìn)行登錄攔截的話,即使我們跳過(guò)登錄頁(yè)面直接去訪問(wèn)任意一個(gè)頁(yè)面也能訪問(wèn)成功,那么登錄功能就沒有意義,同時(shí)也會(huì)存在安全問(wèn)題,本文就來(lái)介紹一下SpringBoot登錄攔截,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
詳解Java正則表達(dá)式中Pattern類和Matcher類
java.util.regex是一個(gè)用正則表達(dá)式所訂制的模式來(lái)對(duì)字符串進(jìn)行匹配工作的類庫(kù)包。包括兩個(gè)類Pattern和Matcher Pattern,Pattern是一個(gè)正則表達(dá)式經(jīng)編譯后的表現(xiàn)模式。Matcher對(duì)象是一個(gè)狀態(tài)機(jī)器,它依據(jù)Pattern對(duì)象做為匹配模式對(duì)字符串展開匹配檢查。2016-12-12

