Java學習(set接口、HashSet集合)
一、set接口
概念:set接口繼承自Collection接口,與List接口不同的是,set接口所儲存的元素是不重復的。
二、HashSet集合
概念:是set接口的實現類,由哈希表支持(實際上是一個HashMap集合)。HashSet集合元素的提取順序與存儲順序不相同。
采用哈希表數據結構存儲數據,保證元素唯一性的方式依賴於:hashCode()與equals()方法。
2.1哈希表
什麽是哈希表? 鏈表與數組的組合。
哈希表底層使用的也是數組機制,數組中也存放對象,而這些對象往數組中存放時的位置比較特殊,當需要把這些對象給數組中存放時,
那麽會根據這些對象的特有數據結合相應的算法
當向哈希表中存放元素時,需要根據元素的特有數據結合相應的算法,這個算法其實就是Object類中的hashCode方法。由於任何對象都是Object類的子類,所以任何對象有擁有這個方法。即就是在給哈希表中存放對象時,會調用對象的hashCode方法,算出對象在表中的存放位置,這裏需要註意,如果兩個對象hashCode方法算出結果一樣,這樣現象稱為哈希沖突,這時會調用對象的equals方法,比較這兩個對象是不是同一個對象,如果equals方法返回的是true,那麽就不會把第二個對象存放在哈希表中,如果
總結:保證HashSet集合元素的唯一,其實就是根據對象的hashCode和equals方法來決定的。如果我們往集合中存放自定義的對象,那麽保證其唯一,就必須復寫hashCode和equals方法建立屬於當前對象的比較方式。
Hashcode方法用來計算哈希值。
hashCode方法計算圖:
哈希表數組和鏈表的結合圖:
2.2HashSet存儲JavaAPI中的類型元素
給HashSet中存儲JavaAPI中提供的類型元素時,不需要重寫元素的hashCode和equals方法,因為這兩個方法,在JavaAPI的每個類中已經重寫完畢
練習實例:
1.向哈希表添加元素並且打印
import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; public class Demo01 { //哈希表 public static void main(String[] args) { // TODO Auto-generated method stub method1();
method2();
method3();
method4();
} public static void method1(){ HashSet<String> set=new HashSet<String> (); set.add("abc"); set.add("abc"); set.add("ghi"); System.out.println(set); // }
打印結果:因為唯一性,所以只存儲了一個“abc”.
2.打印哈希值
//hashcode 方法(object類中提供) public static void method2(){ String s1=new String("abc"); String s2=new String("abc"); //運行出來的 叫哈希值 運行的hashcode是string類的 System.out.println(s1.hashCode()); System.out.println(s2.hashCode()); }
打印結果:哈希值 hashCode()
3.向哈希表中添加自定義類的元素並打印
自定義Person類:
public class Person { private String name; private int age; public Person(){ } //重載構造方法,創建時賦值 public Person(String name, int age) { super(); this.name = name; this.age = age; } @Override //轉為字符串 public String toString() { return "Person [name=" + name + ", 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 //重寫hashCode()方法 public int hashCode() { return name.hashCode()+age*31; } @Override //重寫equals()方法 public boolean equals(Object obj) { if(this==obj){ return true; } if(obj==null){ return false; } if(obj instanceof Person){ Person p=(Person) obj; return name.equals(p.name)&&age==p.age; } return false; } }
測試類方法:
public static void method3(){ HashSet<Person> set=new HashSet<Person>(); set.add(new Person("a",20)); set.add(new Person("a",10)); set.add(new Person("b",30)); set.add(new Person("b",30)); System.out.println(set); }
運行結果:因為重寫了hashCode方法跟equals方法 所以重復的元素並沒有存儲到集合中
三、LinkedHashSet集合:
我們知道HashSet保證元素唯一,可是元素存放進去是沒有順序的,那麽我們要保證有序,怎麽辦呢?
在HashSet下面有一個子類LinkedHashSet,它是鏈表和哈希表組合的一個數據存儲結構。
實例:叠代器遍歷有序唯一輸出:
//叠代器遍歷有序唯一輸出 public static void method4(){ LinkedHashSet<String> set=new LinkedHashSet <String>(); set.add("a"); set.add("aabbb"); set.add("張三"); set.add("李四"); set.add("a"); set.add("李四"); Iterator it=set.iterator(); while(it.hasNext()){ System.out.println(it.next()); }
運行結果:按照存儲順序打印
四、 判斷集合元素唯一的原理
4.1ArrayList的contains方法判斷元素是否重復原理
ArrayList的contains方法會使用調用方法時,傳入的元素的equals方法依次與集合中的舊元素所比較,從而根據返回的布爾值判斷是否有重復元素。
(true則有,false則無)。此時,當ArrayList存放自定義類型時,由於自定義類型在未重寫equals方法前,判斷是否重復的依據是地址值,
所以如果想根據內容判斷是否為重復元素,需要重寫元素的equals方法。
4.2HashSet的add/contains等方法判斷元素是否重復原理
Set集合不能存放重復元素,其添加方法在添加時會判斷是否有重復元素,有重復不添加,沒重復則添加。
HashSet集合由於是無序的,其判斷唯一的依據是元素類型的hashCode與equals方法的返回結果。規則如下:
先判斷新元素與集合內已經有的舊元素的HashCode值
l 如果不同,說明是不同元素,添加到集合。
l 如果相同,再判斷equals比較結果。返回true則相同元素;返回false則不同元素,添加到集合。
所以,使用HashSet存儲自定義類型,如果沒有重寫該類的hashCode與equals方法,則判斷重復時,使用的是地址值,如果想通過內容比較元素是否相同,
需要重寫該元素類的hashcode與equals方法。
Java學習(set接口、HashSet集合)