java集合(10)——HashSet、LinkedHashSet和TreeSet辨析
阿新 • • 發佈:2019-01-23
Set介面
是Collection的子介面,Set要點:
- 不允許包含相同的元素
- 使用equals方法判斷物件是否相同
- 一個不包含重複元素的 collection。更確切地講,set 不包含滿足 e1.equals(e2) 的元素對 e1 和 e2,並且最多包含一個 null 元素
HashSet類
該類實現的介面:Serializable, Cloneable, Iterable, Collection, Set
HashSet原理
- HashSet是基於HashMap來實現的,操作很簡單,更像是對HashMap做了一次“封裝”,而且只使用了HashMap的key來實現各種特性
- HashSet是通過HashMap實現,整個HashSet的核心就是HashMap。HashMap中的鍵作為HashSet中的值,HashMap中的值通過建立一個假的value來實現。
- 下面是HashSet的部分原始碼
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
/**
* Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
* default initial capacity (16) and load factor (0.75).
* 首先有一個HashMap成員變數,我們在HashSet的建構函式中將其初始化,預設情況下用的是Initial capacity為16,load factory載入因子為0.75
*/
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
public boolean contains(Object o) {
return map.containsKey(o);
}
public int size() {
return map.size();
}
HashSet中的一些基本操作都是呼叫HashMap來實現的。
HashSet注意事項
- HashSet不同步,如果需要使用多執行緒訪問,可以使用
Collections.synchronizedSet()
方法包裝
Set s = Collections.synchronizedSet(new HashSet());
- HashSet類判斷兩個元素是否相等,需要兩個條件,第一個條件:
equals()
的結果為true,第二個條件:hashCode()
的值相等,即對應的元素的hashCode碼要相同。 - HashSet類:向該類中新增元素,排列的順序不確定,因此只有不關心集合中元素的順序時才應該是會用HashSet類來儲存元素。
package set;
import java.util.HashSet;
import java.util.Set;
public class HashSetTest {
public static void main(String[] agrs){
Set s = new HashSet();
s.add(new EqualsObj());
s.add(new EqualsObj());
s.add(new HashCodeObj());
s.add(new HashCodeObj());
s.add(new HashSetObj());
s.add(new HashSetObj());
System.out.println("HashSet Elements:");
System.out.print("\t" + s + "\n");
}
}
class EqualsObj {
public boolean equals(Object obj) {
return true;
}
}
class HashCodeObj {
public int hashCode() {
return 1;
}
}
class HashSetObj {
public boolean equals(Object obj) {
return true;
}
public int hashCode() {
return 2;
}
}
在控制檯輸出的結果如下:
HashSet Elements:
[set.EqualsObj@7852e922, set.HashCodeObj@1, set.HashCodeObj@1, set.HashSetObj@2, set.EqualsObj@6d06d69c]
從輸出結果可以看出,1.HashSet類儲存資料的順序是不確定的,2. 該類只認為hashCode和equals方法的值都不同的物件為不同的物件
當我們使用HashSet集合時,需要注意:將物件儲存在HashSet之前,要確保物件重寫了
equals()
和hashCode()
方法,即如果要把一個物件放在HashSet中,如果重寫了該物件的equals()
方法,也要重寫該物件的hashCode()
方法,修改的規則是:如果兩個物件通過equals方法比較返回true時,這兩個物件的hashCode一定也要相同
- HashSet集合的優點:
它可以通過一個物件快速查詢到集合中的物件。hash演算法的價值在於查詢速度很快:它是通過將物件轉變為對應的hashCode值,然後按照hashCode值在桶中對應位置取出該元素。
下面的程式來自:http://blog.csdn.net/shb_derek1/article/details/8729605
package set;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetTest2 {
public static void main(String[] args) {
Set sets = new HashSet();
sets.add(new HashSet2(1));
sets.add(new HashSet2(-11));
sets.add(new HashSet2(21));
sets.add(new HashSet2(13));
sets.add(new HashSet2(-1));
//HashSet是無序集合,因此列印的結果是無序的
System.out.println(sets);
Iterator i = sets.iterator();
//取出集合中第一個元素元素
HashSet2 hs = (HashSet2) i.next();
//將取出的元素賦新值,賦的值與集合中原有的元素之相同
hs.count = 21;
//集合中有兩個元素一樣
System.out.println(sets);
//從集合中移出值
sets.remove(new HashSet2(21));
System.out.println(sets);
//集合中不包含21,因為按照hashCode值找到的槽內沒有該值。
System.out.println("sets.contains(new HashSets(21):"+sets.contains(new HashSet2(21)));
}
}
class HashSet2 {
int count;
public HashSet2(int count) {
super();
this.count = count;
}
public String toString() {
return "HashSet2 [count=" + count + "]" ;
}
public boolean equals(Object obj) {
if(obj instanceof HashSet2){
HashSet2 hs = (HashSet2) obj;
if(this.count == hs.count)
return true;
return false;
}
return false;
}
public int hashCode() {
return this.count;
}
}
執行結果:
[HashSet2 [count=-1], HashSet2 [count=1], HashSet2 [count=21], HashSet2 [count=-11], HashSet2 [count=13]]
[HashSet2 [count=21], HashSet2 [count=1], HashSet2 [count=21], HashSet2 [count=-11], HashSet2 [count=13]]
[HashSet2 [count=21], HashSet2 [count=1], HashSet2 [count=-11], HashSet2 [count=13]]
sets.contains(new HashSets(21):false
LinkedHashSet類
該類實現的介面:Serializable, Cloneable, Iterable, Collection, Set
- 該類是一種可以記住元素插入次序的類,即輸出順序與插入順序一致
- 它是HashSet類的子類,所以也是通過HashCode的值來決定元素的位置
package set;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSet1 {
public static void main(String[] args) {
Set s = new LinkedHashSet();
s.add(new String("e"));
s.add(new String("b"));
s.add(new String("a"));
s.add(new String("c"));
System.out.println(s);
}
}
上述程式執行結果:
[e, b, a, c]
TreeSet類
該類實現的介面:Serializable, Cloneable, Iterable, Collection, NavigableSet, Set, SortedSet
- TreeSe使用紅黑樹的結構實現,集合中的元素進行排序,新增、刪除等演算法的複雜度為O(log(n))
- 使用該集合的類必須實現Comparable介面中的
compare To()
方法,因為該類是有序的 - 該類是通過元素的值進行排序的,而不是通過插入元素的順序進行排序的
如下面程式碼:
package set;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet nums = new TreeSet();
nums.add(new Bird(1));
nums.add(new Bird(2));
nums.add(new Bird(3));
nums.add(new Bird(4));
System.out.println(nums);
}
}
class Bird {
int size;
public Bird(int size) {
super();
this.size = size;
}
public String toString() {
return "Bird [size=" + size + "]";
}
}
執行後,編譯器報錯:
Exception in thread "main" java.lang.ClassCastException: set.Bird cannot be cast to java.lang.Comparable
at java.util.TreeMap.put(TreeMap.java:542)
at java.util.TreeSet.add(TreeSet.java:238)
at set.TreeSetTest.main(TreeSetTest.java:11)
因為TreeSet是實現SortedSet介面的類,因此他有排序功能,對應的傳遞進來的物件也要有需要可比較。先修改上述程式碼如下:
package set;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
TreeSet nums = new TreeSet();
nums.add(new Bird(1));
nums.add(new Bird(2));
nums.add(new Bird(3));
nums.add(new Bird(4));
System.out.println(nums);
}
}
class Bird implements Comparable<Bird>{
int size;
public Bird(int size) {
super();
this.size = size;
}
public String toString() {
return "Bird [size=" + size + "]";
}
public int compareTo(Bird o) {
// TODO Auto-generated method stub
return this.size - o.size;
}
}
程式執行結果:
[Bird [size=1], Bird [size=2], Bird [size=3], Bird [size=4]]