天天看點

集合架構 HashSet集合集合架構——HashSet

集合架構——HashSet

在介紹HashSet集合之前我們先了解下 equals == 和 toString

1,淺談equals與 == 的差別

Java中測試兩個變量相等有兩種方式 “==” 和 equals方法。

對于基本類型變量  == 比較的是字面值(必須都是數值型變量)

對于引用類型  == 比較的是 引用的值(一個位址)

對于自己建立的類,繼承的是Object 類的equals方法,如果該類不覆寫

equals方法,那麼調用時,它還是會去比較對象引用的值(即指向的位址)

對于jar包中的類,哪些類覆寫了該方法,哪些沒有,到具體的時候可以去檢視

該類的API文檔,我們隻需要知道equasl 與 == 的差別即可。

現在有下列需求:

定義兩個學生對象 

1,包含姓名 年齡 學号

2,不指定比較方式,比較兩個學生對象是否是同一對象。

代碼如下:

class Student
{
	private String name;
	private int age;
	private String idStr;
	public Student (String name,int age,String idStr){
		this.name = name;
		this.age = age;
		this.idStr = idStr;
	}
	public String getName(){
		return this.name;
	}
	public int getAge(){
		return this.age;
	}
	public String getIdStr(){
		return this.idStr;
	}
}
public class MyEquals
{
	public static void main(String args[]){
		Student stu1 = new Student("李磊",24,"0912");
		Student stu2 = new Student("韓梅梅",22,"0934");
		System.out.println(stu1 == stu2);
		System.out.println(stu1.equals(stu2));
	}
}
           

2,HashSet集合

HashSet集合底層資料結構是哈希表。

HashSet是如何保證元素的唯一性了?

是同過調用存入集合中元素自身的兩個方法:hashCode 和 equals 方法

在這之前,我們先明确兩個問題:

2.1 hashCode值 和 對象的位址 二者的聯系和差別

 對于自定義類

 當建立一個自定義類,它會繼承hashCode方法。當我們調用hashCode方法時,會傳回一個int資料?這個資料是怎麼得來的了?它與很多内容

 相關,與這個對象的記憶體位址,對象上的成員變量等都有關,這些都參與了哈希值的運算。是以可以明确的是:哈希值不是對象的位址。怎

樣判斷引用是否相同?其實是比較引用指向的位址是否相同。

 如果簡單的認為hashCode傳回的值是位址值,那麼覆寫此方法,使其傳回值都相同,那麼所有建立的對象不是都相同嗎?顯然不是。

2.2equals方法到底在比什麼

   很多地方都提到了 equals方法與 == 的差別 , == 這個操作符是java固有的操作符,它的比較方式是固定的:基本類型數值比較,

引用類型比較。都有自己的規定和計算方法。 但是對于equals方法,它是一個方法,所有類繼承自Object類。由于重載的原因,使其

成為一個很靈活的比較方法,你可以按自己的想法去指定兩個對象如何相等。

這時需要關心的是,檢視誰的equals方法:誰調用檢視誰,看它指定的比較方式。

看下面一段代碼:

public class MyHashSet
{
public static void main(String args[]){
   E element = new E();
   E element2 = new E();
   String str = new String("這不合邏輯!");
       
   sop(element.hashCode());
   sop(element2.hashCode());
   sop(str.hashCode());//String 覆寫了hashCode方法,有自己計算哈希值的方法
   sop(element == element2);
   sop(element.equals(element));
   //sop(element == str);這句在編譯時就無法通過。
   sop(element.equals(str));
}
public static void sop(Object obj){
System.out.println(obj);
}
}
class E
{
public boolean equals(Object obj){
//你可以很不負責的讓兩個對象“相等”
return true;
}
public int hashCode(){
return 60;
} 
}
           

---------- 運作java程式 ----------

60

60

-342913705

false

true

True

從運作結果來看:== 檢查的是兩個對象的位址值是否相同,對于不同類型在編譯時就會出錯。

而指定的equals方法,理論上可以使任何對象“相等”,不過這種相等并無實際意義。

2.3 HashSet集合的存儲方式

有了以上内容做鋪墊,我們在來看看HashSet是如何保證元素的唯一性,就比較清楚了。

你可以把HashSet集合看成是一個裝東西的容器:玻璃瓶,罐子都行。隻是在裝入東西時,要按這個容器的規則進行。

當我們建立一個對象,以自定義對象為例。因為有hashCode方法,這個對象總會産生一個哈希值,不管你的自定義類覆寫hashCode方法與否。

當我們向HashSet容器放元素(或者對象,其實是對象的引用)時,元素放在哪是一個問題。HashSet這個集合的特點是,每個元素都有哈希值,依據元素的哈希值,決定存放位置。

在進行比較時,先調用的是hashCode方法。

當向HashSet集合中存儲元素時,新添加的元素會與已存入的元素做一一比較:

   如果比較的兩個元素通過自身hashCode方法算出的哈希值相同,進一步調用    自身的equals方法,如果equals方法傳回值為真,那麼這兩個元素被判定為    同一進制素,如果equals方法傳回假,兩個元素判定為不同。隻是在集合中存    儲位置上有一個關聯。

   如果比較的兩個元素通過自身的hashCode方法算出的哈希值不同,那麼不調    用equals方法,也就判定這兩個元素不同。

是以可以進行實驗:

自定義一個類,不覆寫hashCode方法和equals 方法。那麼它會沿用Object類中計算哈希值的方法和equals方法。由于hashCode方法的實作很複雜(目前仍未看懂),但是其計算方式可以最大程度防止出現相同哈希值相同。

至于equals方法就比較簡單,我們可以看看:

public boolean equals (Object obj){

      return this == obj;

}也就是檢查其實否為同一對象。

看代碼如何實作:

import java.util.HashSet;
public class MyHashSet2
{
public static void main(String args[]) throws Exception{
HashSet con = new HashSet();
con.add(new Student(21,"Niki"));
con.add(new Student(24,"Caesar"));
con.add(new Student(22,"HanMeiMei"));
con.add(new Student(24,"Caesar"));
sop(con.size());
}
public static void sop(Object obj){
System.out.println(obj.toString());
}
}
class Student
{
private int age;
private String name;
public Student(int age,String name){
super();
this.age = age;
this.name = name;
}
}
           

---------- 運作java程式 ----------

4

通過結果可以看出,四個Student對象都存入到集合中了。

現在的需求變了,當學生的姓名和年齡相同時,就判定為相同對象(肯定不能用 == ,它是在比較位址值,當建立對象時,就會為其開辟存儲空間,這種實體空間肯定不相同)。如何改寫hashCode方法了??原則上hashCode的計算公式中要包含作為比較标準的那些參數:此處是年齡和姓名。

看看代碼是如何實作的:

import java.util.HashSet;
public class MyHashSet2
{
public static void main(String args[]) throws Exception{
HashSet con = new HashSet();
con.add(new Student(21,"Niki"));
con.add(new Student(24,"Caesar"));
con.add(new Student(22,"HanMeiMei"));
con.add(new Student(24,"Caesar"));
sop(con.size());
}
public static void sop(Object obj){
System.out.println(obj.toString());
}
}
class Student
{
private int age;
private String name;
public Student(int age,String name){
super();
this.age = age;
this.name = name;
}
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
public boolean equals(Object obj){ //指定比較方式
if(!(obj instanceof Student))
return false;
Student st = (Student)obj;
return this.name.equals(st.name) && this.age == st.age;
}
public int hashCode(){ //用年齡和姓名做參數計算哈希值
        return (name.hashCode()+age*39);
}
}
           

---------- 運作java程式 ----------

3

輸出完成 (耗時 0 秒) - 正常終止

看到輸出結果是3,證明按照指定的方式實作了元素的存儲。