內容學習於:edu.aliyun.com
1. Set接口簡介
Set接口與List接口一樣都屬於Collection子接口,但是Set接口裏面最大特點在於不能夠進行重複元素的數據保存,首先來觀察Set接口定義:
- public interface Set extends Collection
在JDK 1.9以前,Set 接口並沒有對Collection接口的方法進行任何的擴充,即:兩個接口的方法完全相同的(Set接口沒有List接口中的get)方法),只不過從JDK 1.9 開始,爲其追加了一些of()方法。
使用of方法觀察Set代碼:
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
Set<String> data = Set.of("HELLO", "HELLO");
}
}
結果:
Exception in thread “main” java.lang.IllegalArgumentException: duplicate element: HELLO
此時的程序裏面可以發現直接拋出了一個“IllegalArgumentException "異常,因爲所設置的內容包含有重複數據,而在沒有重複數據的情況下纔可以實現保存。
代碼:
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
Set<String> data = Set.of("hello", "MLDN");
System.out.println(data);
}
}
結果:
[MLDN, hello]
與之前List最大的差別在於,List 中的數據增加順序就屬於保存順序,而發現Set集合裏面會改變數據的存儲順序。
此時創建的Set集合無法實現內容的修改,在實際中.一般都會使用Set接口的子類進行實例化處理,常用的子類:HashSet(散列存儲)、TreeSet ( 有序存儲)、LinkedHashSet (鏈表存儲)。
如下圖所示:
2. HashSet子類(90%)
在Set集合使用的過程裏面,HashSet是一個最爲常見的Set接口子類,也是在以後開發中主要使用的一個類型,來觀察HashSet類定義的結構
- public class HashSet extends AbstractSetimplements Set
如下圖所示:
通過內部的源代碼可以發現,實際上HashSet 裏面包含有一- 個HashMap的結構(key = value的偶對象)。
所以每當使用HashSet無參構造的時候,HashSet之中的默認大小爲16, 而且每當增長到75%的時候會自動進行擴容。
3. TreeSet子類
TreeSet是屬於一種排序的操作結構,所以裏面所保存的內容全部都要求有序存儲,此類的繼承結構如下:
- public class TreeSet extends AbstractSetimplements NavigableSet
- public interface NavigableSet extends SortedSet
- public interface SortedSet extends Set
如下圖所示:
這個時候將按照自然順序(由低到高的升序)進行所有存儲數據的排序,同時也不保存有重複的內容。
4. 集合排序說明
使用TreeSet子類存儲的所有的數據內容都是可以進行有序存儲的,但是所有數據的排序都需要設置一個排序的規則,而這個排序的規則可以使用兩個比較器完成,觀察TreeSet的構造方法
- 使用的是Comparable排序接口: public TreeSet()
- 設置格外使用的Comparator接口: public TreeSet(Comparator<? super E> comparator)
使用TreeSet保存對象代碼:
class Ball implements Comparable<Ball> {
private String name;
private double price;
public Ball(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public int compareTo(Ball o) {
if (this.price != o.price) {
return (int) (this.price - o.price);
} else {
return this.name.compareTo(o.name);
}
}
@Override
public String toString() {
return "Ball{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
}
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
Set<Ball> data = new TreeSet<>();
data.add(new Ball("aaa", 1.2));
data.add(new Ball("bbb", 1.2));
data.add(new Ball("ccc", 1.2));
System.out.println(data);
}
}
結果:
[Ball{name=‘aaa’, price=1.2}, Ball{name=‘bbb’, price=1.2}, Ball{name=‘ccc’, price=1.2}]
在以後的項目開發過程裏面基本上都是針對於數據庫中的數據進行排序,所以如果不是必須的情況下,不要輕易使用TreeSet子類,尤其是使用其保存自定義類對象的場景。
5. 重複元素判斷
Set集合最大的特點在於不會進行重複元素的存儲,通過之前的代碼可以發現TreeSet子類依據的是Comparable接口實現了重複元素的判斷,這個重複元素並不是針對於所有的集合類。
真正的重複元素的判斷實際上需要兩個操作的支持:
- 進行對象編碼的獲取: public int hashCode();
- 進行對象內容的比較: public boolean equals(Object obj)。
對於hashCode(需要有一個計算的公式,通過屬性計算出來一個不會重複的編碼,利用開發工具可以自動生成,在IDEA裏面使用“ALT + INSERT" (在類中輸入),則可以出現一個生成框。
實現代碼:
class Ball {
private String name;
private double price;
public Ball(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public String toString() {
return "Ball{" +
"name='" + name + '\'' +
", price=" + price +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Ball ball = (Ball) o;
return Double.compare(ball.price, price) == 0 &&
name.equals(ball.name);
}
@Override
public int hashCode() {
return Objects.hash(name, price);
}
}
public class JavaAPIDemo {
public static void main(String[] args) throws Exception {
Set<Ball> data = new HashSet<>();
data.add(new Ball("aaa", 1.2));
data.add(new Ball("bbb", 1.2));
data.add(new Ball("ccc", 1.2));
data.add(new Ball("ccc", 1.2));//重複元素
System.out.println(data);
}
}
結果:
[Ball{name=‘ccc’, price=1.2}, Ball{name=‘bbb’, price=1.2}, Ball{name=‘aaa’, price=1.2}]
帶有比較功能的集合類是依據比較器區分的重複,而沒有比較功能的集合利用的就是hashCode()和equals()方法實現。