Set·無序,不重復
HashSet
特點:沒有重復數據,數據不按存入的順序輸出。
HashSet由Hash表結構支持。不支持set的迭代順序,不保證順序。
但是Hash表結構查詢速度很快。
創建集合使用代碼:
Set<String> s = new HashSet<>();
代碼演示:常用方法和遍歷輸出
import java.util.*;
public class TestHashSet {
public static void main(String[] args) {
m010賦值And遍歷();
}
public static void m010賦值And遍歷() {
System.out.println("=====賦值And遍歷");
Set<String> s = new HashSet<>();
s.add("孫悟空");
s.add("小白龍");
s.add("豬八戒");
s.add("沙悟凈");
s.add("孫悟空");
System.out.println("是否為空:" + s.isEmpty());
System.out.println("是否包含:" + s.contains("小白龍"));
System.out.println("移除:" + s.remove("小白龍"));
// (1)foreach:遍歷set
for (String str : s) {
System.out.println(str);
}
// (2)迭代器:遍歷set
Iterator<String> it = s.iterator();
while (it.hasNext()) {
String str = it.next();
System.out.println("Iterator : " + str);
}
// (3)*Java 8新增遍歷方法
s.forEach(elm -> System.out.println("Lambda:" + elm));
}
}
Hash和Hash表
Hash
HashCode,是一個十進制整數,是對象的地址值(邏輯地址,不是物理地址)
Object類有一個方法,可以獲取對象的Hash值。
public native int hashCode();
String重寫了hashCode方法
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
以至於有些不同字符串的hashCode相等(字符串還是不等的,無論==還是equals):
System.out.println("重地".hashCode());
System.out.println("通話".hashCode());
System.out.println("--------------");
System.out.println("方面".hashCode());
System.out.println("樹人".hashCode());
System.out.println("--------------");
System.out.println("兒女".hashCode());
System.out.println("農豐".hashCode());
System.out.println("--------------");
System.out.println("Ea".hashCode());
System.out.println("FB".hashCode());
Hash表
Java8之前:Hash表使用數組+鏈表;
Java8之后加入了紅黑樹,查詢速度加快。
Hash表結構的示意圖如下所示:
數組里存儲的是HashCode。
HashCode相同的元素加入相同鏈表;
如果鏈表長度超過8,就轉為紅黑樹以提高查詢速度。
怎么才算重復?
HashSet中,元素不重復指“equals方法比較為true,且hashCode不同”。
下列示例代碼中,只有equals為true、且hashCode相同的對象未重復存入Set中。
import java.util.*;
class A_equalsT {
public boolean equals(Object obj) {
return true;
}
}
class B_hash1 {
public int hashCode() {
return 1;
}
}
class C_hash2_equalsT {
public int hashCode() {
return 2;
}
public boolean equals(Object obj) {
return true;
}
}
public class TestSet怎么才算重復 {
public static void main(String[] args) {
Set<Object> _set = new HashSet<Object>();
_set.add(new A_equalsT());
_set.add(new A_equalsT());
_set.add(new B_hash1());
_set.add(new B_hash1());
_set.add(new C_hash2_equalsT());
_set.add(new C_hash2_equalsT());
for (Object object : _set) {
System.out.println(object);
}
}
}
運行結果:(C_hash2_equalsT只存入一份,說明被看作重復對象)
B_hash1@1
B_hash1@1
C_hash2_equalsT@2
A_equalsT@15db9742
A_equalsT@6d06d69c
HashSet存儲自定義元素時,需要重寫hashCode和equals方法,才能保證集合中對象的唯一性。
練習:
創建Student類,至少需要包含id、name。創建多個Student對象加入HashSet,如果學號(id)相同則不重復加入。
TreeSet
TreeSet支持兩種排序方式,自然排序和定制排序。默認為自然排序,和HashSet的輸出順序不一樣。
TreeSet使用紅黑樹存儲元素(示例和下一小節一起)。
LinkedHashSet
Set也可以有序,這個LinkedHashSet就能按照輸入順序輸出結果。
LinkedHashSet也是根據元素的hashCode值決定元素的存儲位置,但是加了一條鏈表記錄元素的存儲順序,這使得元素有序。
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
public class TestTree{
public static void main(String[] args) {
m020各種Set();
}
private static void m020各種Set() {
System.out.println("|-HashSet:");
Set<String> _set;
_set = new HashSet<String>();
_set.add("B");
_set.add("A");
_set.add("1");
_set.add("2");
_set.add(null);
printSet(_set);
System.out.println("|-TreeSet不接受空值:");
_set = new TreeSet<String>();
_set.add("B");
_set.add("A");
_set.add("1");
_set.add("2");
// _set.add(null);
printSet(_set);
System.out.println("|-LinkedHashSet:有序");
_set = new LinkedHashSet<String>();
_set.add("B");
_set.add("A");
_set.add("1");
_set.add("2");
_set.add(null);
printSet(_set);
}
private static void printSet(Set<String> _set) {
for (String str : _set) {
System.out.print(str + " ");
}
System.out.println();
}
}
|-HashSet:
null A 1 B 2
|-TreeSet不接受空值:
1 2 A B
|-LinkedHashSet:有序
B A 1 2 null
Set的性能
HashSet綜合效率最高,LinkedHashSet因為有鏈表,遍歷時會更快一些。TreeSet因為要維護紅黑樹,效率較低。