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

1.創(chuàng)建一個默認(rèn)長度16,默認(rèn)加載因子0.75的數(shù)組,數(shù)組名table
- 加載因子:擴(kuò)容時機(jī)
- 當(dāng)存入數(shù)據(jù)達(dá)到16 x 0.75 = 12時候,數(shù)組擴(kuò)容成原數(shù)組的二倍
- JKD8以后:當(dāng)鏈表長度大于8而且數(shù)組長度大于等于64,當(dāng)前鏈表自動轉(zhuǎn)成紅黑樹,增加查找效率
2.根據(jù)元素的哈希值跟數(shù)組的長度計(jì)算出應(yīng)存入的位置
- int index = (數(shù)組長度 - 1) & 哈希值;
- 如果集合中存儲的是自定義對象,必須要重寫hashCode和equsl方法
3.判斷當(dāng)前位置是否為null,如果是null直接存入(添加元素)
4.如果位置不是null,表示有元素,則調(diào)用equals方法比較屬性值
- 一樣:不存 ------ 不一樣:存入數(shù)組,形成鏈表
- JDK8以前:新元素存入數(shù)組,老元素掛在新元素下面
- JDK8以后:新元素掛在老元素下面
HashSet的三個問題
- 問題1.HashSet為什么存和取的順序不一樣?
- HashSet遍歷是從數(shù)組的0索引開始,一條鏈表一條鏈表(或紅黑樹)的遍歷,可能和存儲時候的順序不一樣
- 問題2.HashSet為什么沒有索引?
- HashSet不夠純粹,在底層是鏈表 數(shù)組 紅黑樹三種組合,定義誰都不合適。
- 假設(shè)使用數(shù)組的索引,但一個索引處可能會有鏈表,不可能鏈表上所有的數(shù)據(jù)都是一個索引
- 問題3.HashSet是利用什么機(jī)制保證數(shù)據(jù)去重復(fù)的
- HashCode方法 --> 哈希值 --> 確定元素添加在哪個位置
- equals方法 --> 比較內(nèi)部的屬性值是否相同(自定義對象要重寫這兩種方法)
練習(xí):利用HashSet集合去除重復(fù)元素
需求:創(chuàng)建一個存儲學(xué)生對象的集合,存儲多個學(xué)生對象 使用程序?qū)崿F(xiàn)在控制臺遍歷該集合
要求:學(xué)生對象的成員變量值相同,我們就認(rèn)為是同一個對象
public class A02_HashSetDemo2 {
public static void main(String[] args) {
//1。創(chuàng)建3個學(xué)生對象
Student s1 = new Student("朵朵", 5);
Student s2 = new Student("小七", 7);
Student s3 = new Student("鋼镚", 9);
Student s4 = new Student("朵朵", 5);
//2。創(chuàng)建集合用來添加學(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ù)、無索引
- 這里的有序指的是保證存儲和取出的元素順序一致
- 原理:底層數(shù)據(jù)結(jié)構(gòu)依然是哈希表,只是每個元素又額外多了一個雙鏈表的機(jī)制記錄存儲的順序
public class A04_LinkedHashSetDemo {
public static void main(String[] args) {
//1。創(chuàng)建3個學(xué)生對象
Student s1 = new Student("朵朵", 5);
Student s2 = new Student("小七", 7);
Student s3 = new Student("鋼镚", 9);
Student s4 = new Student("朵朵", 5);
//2。創(chuàng)建集合用來添加學(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring學(xué)習(xí)之開發(fā)環(huán)境搭建的詳細(xì)步驟
本篇文章主要介紹了Spring學(xué)習(xí)之開發(fā)環(huán)境搭建的詳細(xì)步驟,具有一定的參考價值,有興趣的可以了解一下2017-07-07
Java?IO流之StringWriter和StringReader用法分析
這篇文章主要介紹了Java?IO流之StringWriter和StringReader用法分析,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringBoot項(xiàng)目yml配置文件不自動提示解決方案
這篇文章主要介紹了SpringBoot項(xiàng)目配置文件.yaml/.yml文件編寫時沒有自動提示的解決方案,文章通過圖文結(jié)合的方式給大家講解的非常詳細(xì),需要的朋友可以參考下2024-06-06
Java中使用Hutool的DsFactory操作多數(shù)據(jù)源的實(shí)現(xiàn)
在Java開發(fā)中,管理多個數(shù)據(jù)源是一項(xiàng)常見需求,Hutool作為一個全能的Java工具類庫,提供了DsFactory工具,幫助開發(fā)者便捷地操作多數(shù)據(jù)源,感興趣的可以了解一下2024-09-09

