>[success] # Set
1. `java.util.Set`集合是Collection集合的子集合,與List集合平級,該集合中元素**沒有先后放入次序,且不允許重復**
2. 該集合的主要實現類是:`HashSet`類 和 `TreeSet`類以及`LinkedHashSet`類
3. `HashSet`類的底層是采用**哈希表**進行數據管理的
4. `TreeSet`類的底層是采用**紅黑樹**進行數據管理的
5. `LinkedHashSet`了一個**雙向鏈表**,鏈表中記錄了元素的迭代順序,也就是元素插入集合中的**先后順序,因此便于迭代**
>[info] ## 常用方法
* 和 `Collection` 集合中的方法一樣
|方法聲明 |功能介紹|
|--|--|
|boolean add(E e); |向集合中添加對象|
|boolean addAll(Collection<? extends E> c)|用于將參數指定集合c中的所有元素添加到當前集合中|
|boolean contains(Object o); |判斷是否包含指定對象|
|boolean containsAll(Collection<?> c) |判斷是否包含參數指定的所有對象|
|boolean retainAll(Collection<?> c) |保留當前集合中存在且參數集合中存在的所有對象|
|boolean remove(Object o); |從集合中刪除對象|
|boolean removeAll(Collection<?> c) |從集合中刪除參數指定的所有對象|
|void clear(); |清空集合|
|int size();| 返回包含對象的個數|
|boolean isEmpty(); |判斷是否為空|
|boolean equals(Object o)| 判斷是否相等|
|int hashCode() |獲取當前集合的哈希碼值|
|Object[] toArray()| 將集合轉換為數組|
|Iterator iterator()| 獲取當前集合的迭代器|
>[success] # HashSet 使用
~~~
import java.util.Set;
import java.util.HashSet;
import java.util.LinkedHashSet;
public class HashSetTest {
public static void main(String[] args) {
// Set s1 = new HashSet(); // 默認 Object 泛型
Set<String> s1 = new HashSet<>(); // String 泛型
// add 添加元素
boolean b1 = s1.add("two");
System.out.println("b1 = " + b1); // true 添加成功返回true
System.out.println("s1 = " + s1); // [two]
// 從打印結果上可以看到元素沒有先后放入次序(表面)
b1 = s1.add("one");
System.out.println("b1 = " + b1); // true
System.out.println("s1 = " + s1); // [one, two] [two, one]
// 驗證元素不能重復
b1 = s1.add("one");
System.out.println("b1 = " + b1); // false 元素已經存在返回false
System.out.println("s1 = " + s1); // [one, two]
// 打印有順序且不重復 LinkedHashSet
Set<String> s2 = new LinkedHashSet<>(); // 將放入的元素使用雙鏈表連接起來
b1 = s2.add("two");
System.out.println("b1 = " + b1); // true 添加成功返回true
System.out.println("s2 = " + s2); // [two]
b1 = s2.add("one");
System.out.println("b1 = " + b1); // true 添加成功返回true
System.out.println("s2 = " + s2); // [two, one]
b1 = s2.add("one");
System.out.println("b1 = " + b1); // false 元素已經存在返回false
}
}
~~~
>[info] ## HashSet集合的原理
1. 使用元素調用hashCode方法獲取對應的**哈希碼值**,再由某種哈希算法計算出該元素在數組中的**索引位置**。
2. 若該位置沒有元素,則將該元素直接放入即可
3. 若該位置有元素,則使用新元素與已有元素依次**比較哈希值**,**若哈希值不相同**,則將該元素直接放入。
4. 若新元素與已有元素的哈希值相同,則使用新元素調用**equals方法與已有元素依次比較**
5. 若相等則添加元素失敗,否則將元素直接放入即可
* 哈希表結構

**總結**:當兩個元素調用**equals方法相等時證明這兩個元素相**同,重寫hashCode方法后保證這兩個元素得到的哈**希碼值相同**,由同一個哈希算法生成的索引位置相同,此時只需要與該索引位置已有元素比較即可,從而**提高效率并避免重復元素的出現**
>[success] # TreeSet
1. **TreeSet集合**的底層采用**紅黑樹**進行數據的管理,當有新元素插入到TreeSet集合時,需要使用新元素與集合中已有的元素**依次比較來確定新元素的合理位置**
* 紅黑樹是一種**平衡二叉樹**,二叉樹主要概念,每個節點最多只有兩個子節點的樹形結構,**左子樹中的任意節點元素都小于根節點元素值,右子樹中的任意節點元素都大于根節點元素值**
2. **TreeSet** 遵循紅黑樹的規則,那么就要去實現其比較的規則書寫,這里分兩種
* 使用元素的自然排序規則進行比較并排序,讓元素類型實現**java.lang.Comparable**接口(String 內部就繼承了該接口并實現了 compareTo 方法因此可以從小到大排序)
* 使用比較器規則進行比較并排序,構造TreeSet集合時傳入**java.util.Comparator**接口
>[danger] ##### 案例
1. String 內部就繼承了**java.lang.Comparable**接口并實現了 `compareTo` 方法因此可以從小到大排序
~~~
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet<String> s1 = new TreeSet<>();
// 添加元素
boolean a1 = s1.add("aa");
System.out.println(a1); // true 添加成功
a1 = s1.add("aa");
System.out.println(a1); // false 已存在添加失敗
s1.add("cc");
s1.add("bb");
// TreeSet集合的底層是采用紅黑樹實現的,因此元素有大小次序,默認從小到大打印
System.out.println(s1); // [aa, bb, cc]
}
}
~~~
>[danger] ##### 自定義類實現**java.lang.Comparable**接口
1. 如果沒有去實現自定類中**java.lang.Comparable**接口 直接使用**TreeSet** 打印會提示報錯`Exception in thread "main" java.lang.ClassCastException: class cannot be cast to class java.lang.Comparable`
2. Set 是**不允許重復**,因此比較時符合 `compareTo` 規則重復會被忽略
~~~
import java.util.TreeSet;
public class Person implements Comparable<Person> {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person o) {
// return 0; // 調用對象和參數對象相等,調用對象就是新增加的對象
// return -1; // 調用對象小于參數對象
// return 1; // 調用對象大于參數對象
// return this.getName().compareTo(o.getName()); // 比較姓名
// return this.getAge() - o.getAge(); // 比較年齡
// 姓名相同則按照年齡比價
int ia = this.getName().compareTo(o.getName());
return ia == 0 ? this.getAge() - o.getAge() : ia;
}
public static void main(String[] args) {
TreeSet<Person> tp = new TreeSet<>();
Person p = new Person("w", 15);
Person p1 = new Person("e", 10);
Person p2 = new Person("r", 16);
Person p3 = new Person("q", 10);
Person p4 = new Person("q", 10);
tp.add(p);
tp.add(p1);
tp.add(p2);
tp.add(p3);
Boolean b = tp.add(p4);
System.out.print(b); // false
// 自定義類如果沒用實現接口 java.lang.Comparable 會報錯
// Exception in thread "main" java.lang.ClassCastException: class Person cannot
// be cast to class java.lang.Comparable
System.out.println(tp); // [Person{name='e', age=10}, Person{name='q', age=10}, Person{name='r', age=16}, Person{name='w', age=15}] 重復的比較規則P4 被忽略
}
}
~~~
>[danger] ##### java.util.Comparator 接口形式比較
1. 自定義實現類沒有實現接口**java.lang.Comparable**,但通過 `Lambda` 表達式 和`匿名內部類` 作為比較條件參數傳入
~~~
import java.util.TreeSet;
import java.util.Comparator;
public class Person {
private String name;
private int age;
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public static void main(String[] args) {
// // 準備一個比較器對象作為參數傳遞給構造方法
// // 匿名內部類: 接口/父類類型 引用變量名 = new 接口/父類類型() { 方法的重寫 };
// Comparator<Person> c = new Comparator<Person>() {
// @Override
// public int compare(Person o1, Person o2) { // o1表示新增加的對象 o2表示集合中已有的對象
// return o1.getAge() - o2.getAge(); // 表示按照年齡比較
// }
// };
// 使用從Java8開始支持Lambda表達式: (參數列表) -> { 方法體 }
Comparator<Person> c = (Person o1, Person o2) -> {
return o1.getAge() - o2.getAge();
};
TreeSet<Person> tp = new TreeSet<>(c);
Person p = new Person("w", 15);
Person p1 = new Person("e", 10);
Person p2 = new Person("r", 16);
Person p3 = new Person("q", 10);
Person p4 = new Person("q", 10);
tp.add(p);
tp.add(p1);
tp.add(p2);
tp.add(p3);
tp.add(p4);
/*
* [Person{name='e', age=10}, Person{name='w', age=15}, Person{name='r',
* age=16}]
*/
System.out.println(tp);
}
}
~~~
>[danger] ##### 總結
自然排序的規則比較單一,而比較器的規則比較多元化,而且比較器優先于自然排序
- windows -- 環境變量
- Vscode -- 編寫java
- 初始java
- java -- 關鍵字
- 編寫第一個java程序
- java -- 注釋
- 計算機存儲 -- 進制
- java -- 類型
- java -- 變量
- 數字類型
- 布爾類型
- 字符類型
- 類型轉換
- 雙等比較是什么
- java -- 運算符
- 算數運算符
- 字符串拼接
- 關系/比較運算符
- 自增減運算符
- 邏輯運算符
- 三目運算
- 賦值運算符
- 移位運算符
- 位運算符
- 運算符優先級
- java -- 流程控制語句
- if /else if /if -- 判斷
- switch case分支結構
- for -- 循環
- 用雙重for循環
- while -- 循環
- do while -- 循環
- 案例練習
- java -- 數組
- 數組的存儲
- 數組的增刪改查
- 數組的特點
- 數組案例
- 二維數組
- 數組的工具方法
- java -- 方法
- java -- 方法的重載
- java -- 方法的調用流程
- java -- 類方法傳參注意事項
- java -- 方法練習案例
- 對比 return break continue
- for each循環
- java -- 基礎練習
- java -- 面向對象
- java -- 創建類和對象
- java -- 訪問控制符
- java -- 類成員方法
- java -- 構造方法
- java -- this
- java -- 封裝
- java -- 對象內存圖
- java -- 創建對象案例
- java -- static
- java -- 繼承
- super -- 關鍵字
- java -- 構造塊和靜態代碼塊
- java -- 重寫
- java -- final
- java -- 多態
- java -- 抽象類
- java -- 接口
- 引用類型數據轉換
- 綜合案例
- java -- 內部類
- java -- 回調模式
- java -- 枚舉類型
- java -- switch 使用枚舉
- java -- 枚舉方法使用
- java -- 枚舉類實現接口
- java -- javaBean
- java -- package 包
- java -- import
- java -- 遞歸練習
- java -- 設計模式
- 單例模式
- java -- 注解
- java -- 元注解
- Java -- 核心類庫
- java -- 處理字符串
- Java -- String
- String -- 常用方法
- String -- 正則
- Java -- StringBuilder 和 StringBuffer
- 知識點
- Java -- StringJoiner 字符串拼接
- 練習題
- 字符串的總結
- Java -- 包裝類
- Integer
- Double
- Boolean
- Character
- java -- 集合類
- java -- util.Collection
- Iterator接口
- java -- util.List
- java -- ArrayList
- java -- util.Queue
- java -- util.Set
- java -- util.Map
- java -- util.Collections
- Java -- Math
- Java -- java.lang
- Java -- Object
- Java -- 獲取當前時間戳
- Java -- 異常
- Java -- java.util
- java -- Date
- java -- Calender
- Java -- java.text
- Java -- SimpleDateFormat
- Java -- java.time
- Java -- java.io
- java -- io.File
- java -- 泛型
- IDEA -- 用法