JavaSE丨集合框架入門(二):從 0 掌握 Set 集合

? ? ? ? 這節我們接著學習 Set 集合。

一、Set 集合

1.1 Set 概述

????????java.util.Set 接口繼承了 Collection 接口,是常用的一種集合類型。 相對于之前學習的List集合,Set集合特點如下:

????????除了具有 Collection 集合的特點,還具有自己的一些特點:

  • ?Set是一種無序的集合
  • ?Set是一種不帶下標索引的集合
  • ?Set是一種不能存放重復數據的集合
Set 接口繼承體系:

Set 接口方法:

基礎案例:

實例化一個Set集合,往里面添加元素并輸出,注意觀察集合特點(無序、不重復):

import java.util.Set;
import java.util.HashSet;
import java.util.Iterator;public class SetTest {public static void main(String[] args) {//1.實例化Set集合,指向HashSet實現類對象Set<String> set = new HashSet<>();set.add("hello1");set.add("hello2");set.add("hello3");set.add("hello4");set.add("hello5");set.add("hello5"); //添加失敗 重復元素//增強for循環遍歷for (String obj : set) {System.out.println(obj);}System.out.println("------------------");//迭代器遍歷Iterator<String> it = set.iterator();while (it.hasNext()) {Object obj = it.next();System.out.println(obj);}}
}

運行結果:

hello1
hello4
hello5
hello2
hello3
------------------
hello1
hello4
hello5
hello2
hello3

通過以上代碼和運行結果,可以看出Set類型集合的特點:無序、不可重復

1.2 HashSet

????????java.util.HashSet 是Set接口的實現類,它使用哈希表(Hash Table)作為其底層數據結構來存儲數據。

HashSet特點:
  • 無序性:HashSet中的元素的存儲順序與插入順序無關

HashSet使用哈希表來存儲數據,哈希表根據元素的哈希值來確定元素的存儲位置,而哈希值是根據元素的內容計算得到的,與插入順序無關。

  • 唯一性:HashSet中不允許重復的元素,即每個元素都是唯一的

關鍵:存儲自定義對象時,必須同時重寫hashCode()equals(),否則無法保證唯一性(默認用地址值計算哈希,導致內容相同的對象被視為不同元素)。

  • 允許null元素:HashSet允許存儲null元素,但只能存儲一個null元素, HashSet中不允許重復元素
  • 高效性:HashSet的插入、刪除和查找操作的時間復雜度都是O(1)

哈希表通過將元素的哈希值映射到數組的索引來實現快速的插入、刪除和查找操作。

基礎案例1:

實例化HashSet對象,往里面插入多個字符串,驗證HashSet特點。

import java.util.HashSet;
import java.util.Set;public class HashSetTest {public static void main(String[] args) {//1.實例化HashSet對象Set<String> set = new HashSet<>();//2.添加元素set.add("hello");set.add("world");set.add("nihao");set.add("hello");set.add(null);set.add(null);System.out.println("size:"+set.size());//3.遍歷for (String str : set) {System.out.println(str);}}
}

運行結果:

size:4
null
world
nihao
hello

根據結果可知,HashSet無序、唯一、null值可存在且只能存在一個。

元素插入過程:

當向HashSet中插入元素時,先獲取元素的hashCode()值,再檢查HashSet中是否存在相同哈希值的元素,如果元素哈希值唯一,則直接插入元素

如果存在相同哈希值的元素,會調用元素的equals()方法來進一步判斷元素是否是相同。如果相同,則不會插入重復元素;如果不同,則插入元素

案例展示:

重寫Student類的hashCode()方法:

class Student {private String id;       // 關鍵屬性1(equals中比較)private String name;     // 關鍵屬性2(equals中比較)private int age;         // 非關鍵屬性(equals中不比較,hashCode中也不參與計算)// 構造方法public Student(String id, String name, int age) {this.id = id;this.name = name;this.age = age;}// 先重寫equals()(必須與hashCode()基于相同屬性)@Overridepublic boolean equals(Object obj){if (this == obj) {return true;}if (obj == null) {return false;}if (getClass() != obj.getClass()) {return false;}Student other = (Student)obj;if (name == null) {if (other.name != null) {return false;}}else if (!name.equals(other.name)) {return false;}if (age != other.age) {return false;}return true;}// 重寫hashCode()(基于id和name計算)@Overridepublic int hashCode() {int result = 17; // 初始化哈希種子(非零經驗值)// 處理id(引用類型,可能為null)int idHash = (id == null) ? 0 : id.hashCode();result = 31 * result + idHash; // 31是質數,減少哈希沖突// 處理name(引用類型,可能為null)int nameHash = (name == null) ? 0 : name.hashCode();result = 31 * result + nameHash;return result; // 返回最終哈希值}
}

如果要往HashSet集合存儲自定義類對象,那么一定要重寫自定義類中的 hashCode方法和equals方法!

1.3 TreeSet

????????TreeSet是SortedSet(Set接口的子接口)的實現類,它基于紅黑樹(Red Black Tree)數據結構實現。

TreeSet特點:
  • 有序性:插入的元素會自動排序,每次插入、刪除或查詢操作后,TreeSet會自動調整元素的順序,以保持有序性。
  • 唯一性:TreeSet中不允許重復的元素,即集合中的元素是唯一的。如果嘗試插入一個已經存在的元素,該操作將被忽略。
  • 動態性:TreeSet是一個動態集合,可以根據需要隨時添加、刪除和修改元素。插入和刪除操作的時間復雜度為O(log n),其中n是集合中的元素數量。
  • 高效性:由于TreeSet底層采用紅黑樹數據結構,它具有快速的查找和插入性能。對于有序集合的操作,TreeSet通常比HashSet更高效。
基礎案例:

準備一個TreeSet集合,放入多個自定義類Person對象,觀察是否自動進行排序。

基礎類Person類:

import java.util.Set;import java.util.TreeSet;class Person {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}
}

測試類:

public class Test_Person {public static void main(String[] args) {//1.實例化TreeSetSet<Person> set = new TreeSet<>();//2.添加元素set.add(new Person("zs", 21));set.add(new Person("ls", 20));set.add(new Person("tom", 19));//3.遍歷集合for (Person person : set) {System.out.println(person);}}
}

運行程序發現:

//程序運行,提示異常,具體如下:
Exception in thread "main" java.lang.ClassCastException: 
java.lang.Comparableat java.util.TreeMap.compare(TreeMap.java:1290)at java.util.TreeMap.put(TreeMap.java:538)at java.util.TreeSet.add(TreeSet.java:255)at Test_Person.main(Test_Person.java:41)
問題分析:

根據異常提示可知錯誤原因是: Person 無法轉化成 Comparable ,從而導致 ClassCastException 異常 。 為什么會這樣呢?

解析:

TreeSet是一個有序集合,存儲數據時,一定要指定元素的排序規則,有兩種指定的方式,具體如下:

  • 自然排序(元素所屬類型要實現 java.lang.Comparable 接口)
  • 比較器排序
1)自然排序

如果一個類,實現了 java.lang.Comparable 接口,并重寫了 compareTO 方法,那么這個類的對象就是可以比較大小的。

public interface Comparable<T> {public int compareTo(T o);
}
compareTo方法說明:

????????int result = this.屬性.compareTo(o.屬性);

  • result的值大于0,說明this比o大
  • result的值小于0,說明this比o小
  • result的值等于0,說明this和o相等
元素插入過程:

????????當向TreeSet插入元素時,TreeSet會使用元素的 compareTo() 方法來比較元素之間的大小關系。根據比較結果,TreeSet會將元素插入到合適的位置,以保持有序性。如果兩個元素相等( compareTo() 方法返回0),TreeSet會認為這是重復的元素,只會保留一個

基礎 Person 類:

//定義Person類,實現自然排序
public class Person implements Comparable<Person> {private String name;private int age;public Person() {}public Person(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}//重寫方法,指定比較規則:先按name升序,name相同則按age降序@Overridepublic int compareTo(Person o) {//注意:字符串比較需要使用compareTo方法int r = name.compareTo(o.name);if(r == 0) {r = o.age - this.age;}return r;}
}

測試類:

import java.util.Set;
import java.util.TreeSet;
import com.briup.chap08.bean.Person;//自然排序測試
public class Test073_Comparable {public static void main(String[] args) {//1.實例化TreeSetSet<Person> set = new TreeSet<>();//2.添加元素set.add(new Person("zs", 21));set.add(new Person("ww", 20));set.add(new Person("zs", 21));set.add(new Person("tom", 19));set.add(new Person("tom", 23));set.add(new Person("jack", 20));//3.遍歷集合for (Person person : set) {System.out.println(person);}}
}

運行結果:

Person [name=jack, age=20]
Person [name=tom, age=23]
Person [name=tom, age=19]
Person [name=ww, age=20]
Person [name=zs, age=21]

????????對于整型、浮點型元素,它們會按照從小到大的順序進行排序。對于字符串類型的元素,它們會按照字典順序進行排序。

注意事項:compareTo方法的放回結果,只關心正數、負數、零,不關心具體的值是多少

2)TreeSet 比較器排序

思考:如果上述案例中Person類不是自定義類,而是第三方提供好的(不可以修改源碼),那么如何實現排序規則的指定?

我們可以使用比較器(Comparator)來自定義排序規則。

比較器排序步驟:
  1. 創建一個實現了Comparator接口的比較器類,并重寫其中的 compare() 方法
  2. 該方法定義了元素之間的比較規則。在 compare() 方法中,我們可以根據元素的屬性進行比較,并返回負整數、零或正整數,來表示元素之間的大小關系。
  3. 創建TreeSet對象時,將比較器對象作為參數傳遞給構造函數,這樣,TreeSet 會根據比較器來進行排序。
compare方法說明:

???????? int result = compare(o1, o2);

  • result的值大于0,表示升序
  • result的值小于0,表示降序
  • result的值等于0,表示元素相等,不能插入

注意:這里和自然排序的規則是一樣的,只關心正數、負數、零,不關心具體的值是多少

案例展示:

使用比較器排序,對上述案例中Person進行排序,要求先按age升序,age相同則按name降序。

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;public class TestPerson {public static void main(String[] args) {//1.準備自定義比較器對象//匿名內部類形式Comparator<Person> comp = new Comparator<Person>() {//重寫比較算法:先按age升序,age相同則按name降序@Overridepublic int compare(Person o1,Person o2){int r = o1.getAge() - o2.getAge();if (r == 0) {//注意:比較字符串需使用compareTo()方法r = o2.getName().compareTo(o1.getName());}return r;}};//2.實例化TreeSet,傳入自定義比較器對象Set<Person> set = new TreeSet<>(comp);//3.添加元素set.add(new Person("zs",21));set.add(new Person("ww",20));set.add(new Person("zs",21));set.add(new Person("tom",19));set.add(new Person("tom",23));set.add(new Person("jack",20));//4.遍歷集合for (Person person : set) {System.out.println(person);}}
}

運行結果:

Person [name=tom, age=19]
Person [name=ww, age=20]
Person [name=jack, age=20]
Person [name=zs, age=21]
Person [name=tom, age=23]

注意:如果同時使用自然排序和比較器排序,比較器排序將覆蓋自然排序。

對比項自然排序(Comparable比較器排序(Comparator
實現位置元素類內部實現接口,重寫?compareTo元素類外部創建比較器,重寫?compare
排序規則數量一個類只能有一種自然排序規則一個類可以有多個比較器,實現不同規則
優先級若同時傳比較器,自然排序會被覆蓋傳入比較器時,優先用比較器規則
適用場景元素有固定、單一排序規則(如?Integer?數值)需要臨時、多樣化排序(如有時按年齡,有時按姓名)
代碼侵入性必須修改元素類代碼(讓其實現?Comparable無需修改元素類,外部單獨定義規則

1.4 LinkedHashSet

????????LinkedHashSet 是 HashSet 的一個子類,具有 HashSet 的高效性能和唯一性特性,并且保持了元素的插入順序,其底層基于哈希表和鏈表實現。

案例展示:

實例化一個LinkedHashSet集合對象,存入多個String字符串,觀察是否唯一及順序存儲。

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;public class Test_LinkedHashSet {public static void main(String[] args) {//1.實例化LinkedHashSetSet<String> set = new LinkedHashSet<>();//2.添加元素set.add("bbb");set.add("aaa");set.add("abc");set.add("bbc");set.add("abc");//3.迭代器遍歷Iterator it = set.iterator();while (it.hasNext()) {System.out.println(it.next());}}
}

運行結果:

bbb
aaa
abc
bbc

1.5 Set 小結

下節我們學習Map集合。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/920533.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/920533.shtml
英文地址,請注明出處:http://en.pswp.cn/news/920533.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

金屬結構疲勞壽命預測與健康監測技術—— 融合能量法、紅外熱像技術與深度學習的前沿實踐

理論基礎與核心方法 疲勞經典理論及其瓶頸 1.1.疲勞失效的微觀與宏觀機理&#xff1a; 裂紋萌生、擴展與斷裂的物理過程。 1.2.傳統方法的回顧與評析。 1.3.引出核心問題&#xff1a;是否存在一個更具物理意義、能統一描述疲勞全過程&#xff08;萌生與擴展&#xff09;且試驗量…

【貪心算法】day4

&#x1f4dd;前言說明&#xff1a; 本專欄主要記錄本人的貪心算法學習以及LeetCode刷題記錄&#xff0c;按專題劃分每題主要記錄&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代碼&#xff1b;&#xff08;2&#xff09;優質解法 優質代碼&#xff1b;&#xff…

AI 與腦機接口的交叉融合:當機器 “讀懂” 大腦信號,醫療將迎來哪些變革?

一、引言&#xff08;一&#xff09;AI 與腦機接口技術的發展現狀AI 的崛起與廣泛應用&#xff1a;近年來&#xff0c;人工智能&#xff08;AI&#xff09;技術迅猛發展&#xff0c;已廣泛滲透至各個領域。從圖像識別、自然語言處理到智能決策系統&#xff0c;AI 展現出強大的數…

uniapp vue3 canvas實現手寫簽名

userSign.vue <template><view class"signature"><view class"btn-box" v-if"orientation abeam"><button click"clearClick">重簽</button><button click"finish">完成簽名</butt…

頁面跳轉html

實現流程結構搭建&#xff08;HTML&#xff09;創建側邊欄容器&#xff0c;通過列表或 div 元素定義導航項&#xff0c;每個項包含圖標&#xff08;可使用字體圖標庫如 Font Awesome&#xff09;和文字&#xff0c;為后續點擊交互預留事件觸發點。樣式設計&#xff08;CSS&…

Spring Boot自動裝配機制的原理

文章目錄一、自動裝配的核心觸發點&#xff1a;SpringBootApplication二、EnableAutoConfiguration的作用&#xff1a;導入自動配置類三、自動配置類的加載&#xff1a;SpringFactoriesLoader四、自動配置類的條件篩選&#xff1a;Conditional注解五、自動配置的完整流程六、自…

(未完結)階段小總結(一)——大數據與Java

jdk8-21特性核心特征&#xff1a;&#xff08;8&#xff09;lambda&#xff0c;stream api&#xff0c;optional&#xff0c;方法引用&#xff0c;函數接口&#xff0c;默認方法&#xff0c;新時間Api&#xff0c;函數式接口&#xff0c;并行流&#xff0c;ComletableFuture。&…

嵌入式Linux驅動開發:設備樹與平臺設備驅動

嵌入式Linux驅動開發&#xff1a;設備樹與平臺設備驅動 引言 本筆記旨在詳細記錄嵌入式Linux驅動開發中設備樹&#xff08;Device Tree&#xff09;和平臺設備驅動&#xff08;Platform Driver&#xff09;的核心概念與實現。通過分析提供的代碼與設備樹文件&#xff0c;我們…

【完整源碼+數據集+部署教程】骨折檢測系統源碼和數據集:改進yolo11-EfficientHead

背景意義 骨折作為一種常見的骨骼損傷&#xff0c;其診斷和治療對患者的康復至關重要。傳統的骨折檢測方法主要依賴于醫生的經驗和影像學檢查&#xff0c;如X光、CT等&#xff0c;這不僅耗時&#xff0c;而且容易受到主觀因素的影響。隨著計算機視覺和深度學習技術的迅猛發展&a…

面試記錄7 c++軟件開發工程師

開目 一面&#xff1a; 自我介紹你做的xxx應用是用c做的嗎&#xff0c;是在window平臺嗎具體做的事情是什么你說的2D3D的結構是什么樣的&#xff0c;怎樣去做校驗有沒有二維到三維或者三維到二維的數據轉換兩個向量怎么去做校驗做的什么優化有調用第三方庫嗎是用的什么工具&…

計算機網絡:服務器處理多客戶端(并發服務器)

一、服務器處理多客戶端&#xff08;并發服務器&#xff09;&#xff08;一&#xff09;listen:監聽客戶端的連接請求&#xff0c;放入請求隊列&#xff08;二&#xff09;accpet&#xff1a;請求隊列中提取已連接的請求&#xff0c;返回連接好的fd&#xff08;循環accpet即可&…

Ansible自動化運維:原理以及安裝教程

目錄 Linux Ansible&#xff1a;作用與原理詳解 一、Ansible 的核心作用 1. 配置管理&#xff08;Configuration Management&#xff09; 2. 應用部署&#xff08;Application Deployment&#xff09; 3. 任務編排&#xff08;Orchestration&#xff09; 4. 其他擴展作用 二、A…

[機器學習]基于K-means聚類算法的鳶尾花數據及分類

基于Kmeans&#xff0c;對鳶尾花數據集前兩個特征進行聚類分析通過迭代優化&#xff0c;將150個樣本劃分到K個簇中。目標函數&#xff1a;最小化所有樣本到其所屬簇中心的距離平方和。算法步驟&#xff1a;隨機初始化K個簇中心。將每個樣本分配到最近的中心。計算均值確定每個簇…

Altium Designer 22使用筆記(10)---PCB鋪銅相關操作

目錄 01 | 簡 述 02 | 環境描述 03 | 鋪 銅 04 | 鋪銅挖空 05 | 敷銅合并 06 | 敷銅的修改 07 | 總 結 01 | 簡 述 在PCB設計階段&#xff0c;除了布局、布線操作需要頻繁進行調整外&#xff0c;鋪銅操作的使用也非常頻繁&#xff1b;因此本篇文章的主要內容為&#xff…

leetcode 338 比特位計數

一、題目描述二、解題思路我們可以借助位運算的思想來解決這個問題。通過kk&(k-1)來消除k中最右邊為1的比特位&#xff0c;每次消除后進行count&#xff0c;當k為0時&#xff0c;表示所有的1消除完畢&#xff0c;此時的count即為所有1的個數。三、代碼實現時間復雜度&#…

PHP的md5()函數分析

MD5&#xff08;Message-Digest Algorithm 5&#xff09;是一種廣泛使用的哈希函數&#xff0c;由Ronald Rivest于1991年設計&#xff0c;屬于密碼散列算法家族。其核心功能是將任意長度的輸入數據&#xff08;如字符串、文件等&#xff09;通過不可逆的數學運算轉換為固定長度…

【面試場景題】怎么做業務領域劃分

文章目錄一、核心原則&#xff1a;以業務為中心&#xff0c;而非技術二、具體步驟&#xff1a;從業務理解到邊界定義1. 深入理解業務&#xff1a;梳理業務全景2. 識別核心領域與支撐領域3. 劃分“限界上下文”&#xff1a;定義領域邊界4. 定義領域內的“聚合”&#xff1a;細化…

海量小文件問題綜述和解決攻略(二)

1. 解決NameNode的內存問題 上面的內容提到過每個block的元數據都需要加載到NameNode的內存中&#xff0c;這導致一個Hadoop集群在NameNode中存儲的對象是有上限的&#xff0c;并且對象太多會帶來啟動時間較長以及網絡延遲的問題。常見的有兩種解決方案&#xff0c;減少集群的…

《開發避坑指南:從異常中讀懂系統的“求救信號”》

異常現象從不只是孤立的“故障”&#xff0c;而是系統發出的“健康預警”。太多團隊困在“出現問題-臨時修復-再次復發”的循環里&#xff0c;將精力消耗在表面問題的撲救上&#xff0c;卻忽視了背后潛藏的架構缺陷、邏輯漏洞與環境適配盲區。真正成熟的開發思維&#xff0c;是…

數字孿生技術為UI前端賦能:實現產品性能的實時監測與預警

hello寶子們...我們是艾斯視覺擅長ui設計、前端開發、數字孿生、大數據、三維建模、三維動畫10年經驗!希望我的分享能幫助到您!如需幫助可以評論關注私信我們一起探討!致敬感謝感恩!過去十年&#xff0c;前端技術棧翻天覆地&#xff1a;React/Vue/Angular、Webpack/Vite、Serve…