1. 程式人生 > >關於重寫equals方法,hashcode方法,toString方法 ,compareto()方法

關於重寫equals方法,hashcode方法,toString方法 ,compareto()方法

關於重寫equals方法,hashcode方法,toString方法 ,compareto()方法

總結:

toString說白了,就是為了顯示用的

Compareto 方法為了比較用的

只有用到HashtableHashMapHashSetLinkedHashMap等時才要注意hashcode,其他地方hashcode無用。

Hashcode: 為什麼要重寫hashCode方法?

我們應該先了解java判斷兩個物件是否相等的規則。

在java的集合中,判斷兩個物件是否相等的規則是:
首先,判斷兩個物件的hashCode是否相等

如果不相等,認為兩個物件也不相等
如果相等,則判斷兩個物件用equals運算是否相等
如果不相等,認為兩個物件也不相等
如果相等,認為兩個物件相等

1. 首先equals()hashcode()這兩個方法都是從object類中繼承過來的。
equals()方法在object類中定義如下:
public boolean equals(Object obj) {
return (this == obj);
}
很明顯是對兩個物件的地址值進行的比較(即比較引用是否相同)。但是我們必需清楚,當String Math、還有IntegerDouble。。。。等這些封裝類在使用equals()方法時,已經覆蓋了object類的equals()方法。比如在String類中如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
很明顯,這是進行的內容比較,而已經不再是地址的比較。依次類推Double

IntegerMath。。。。等等這些類都是重寫了equals()方法的,從而進行的是內容的比較。當然了基本型別是進行值的比較,這個沒有什麼好說的。
我們還應該注意,Java語言對equals()的要求如下,這些要求是必須遵循的:
• 對稱性:如果x.equals(y)返回是“true”,那麼y.equals(x)也應該返回是“true”
• 反射性:x.equals(x)必須返回是“true”
• 類推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那麼z.equals(x)也應該返回是“true”
• 還有一致性:如果x.equals(y)
返回是“true”,只要xy內容一直不變,不管你重複x.equals(y)多少次,返回都是“true”
• 任何情況下,x.equals(null),永遠返回是“false”x.equals(x不同型別的物件)永遠返回是“false”
以上這五點是重寫equals()方法時,必須遵守的準則,如果違反會出現意想不到的結果,請大家一定要遵守。
2. 其次是hashcode() 方法,在object類中定義如下:
public native int hashCode();
說明是一個本地方法,它的實現是根據本地機器相關的。當然我們可以在自己寫的類中覆蓋hashcode()方法,比如StringIntegerDouble。。。。等等這些類都是覆蓋了hashcode()方法的。例如在String類中定義的hashcode()方法如下:
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;

for (int i = 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
解釋一下這個程式(StringAPI中寫到):
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
使用 int 演算法,這裡 s[i] 是字串的第 個字元,是字串的長度,表示求冪。(空字串的雜湊碼為 0。)

3.這裡我們首先要明白一個問題:
equals()相等的兩個物件,hashcode()一定相等;
equals()不相等的兩個物件,卻並不能證明他們的hashcode()不相等。換句話說,equals()方法不相等的兩個物件,hashcode()有可能相等。(我的理解是由於雜湊碼在生成的時候產生衝突造成的)。
反過來:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。解釋下第3點的使用範圍,我的理解是在objectString等類中都能使用。在object類中,hashcode()方法是本地方法,返回的是物件的地址值,而object類中的equals()方法比較的也是兩個物件的地址值,如果equals()相等,說明兩個物件地址值也相等,當然hashcode()也就相等了;在String類中,equals()返回的是兩個物件內容的比較,當兩個物件內容相等時,
Hashcode()方法根據String類的重寫(第2點裡面已經分析了)程式碼的分析,也可知道hashcode()返回結果也會相等。以此類推,可以知道IntegerDouble等封裝類中經過重寫的equals()hashcode()方法也同樣適合於這個原則。當然沒有經過重寫的類,在繼承了object類的equals()hashcode()方法後,也會遵守這個原則。

4.談到hashcode()equals()就不能不說到hashset,hashmap,hashtable中的使用,具體是怎樣呢,請看如下分析:
Hashset是繼承Set介面,Set介面又實現Collection介面,這是層次關係。那麼hashset是根據什麼原理來存取物件的呢?
hashset中不允許出現重複物件,元素的位置也是不確定的。在hashset中又是怎樣判定元素是否重複的呢?這就是問題的關鍵所在,經過一下午的查詢求證終於獲得了一點啟示,和大家分享一下,在java的集合中,判斷兩個物件是否相等的規則是:
1),判斷兩個物件的hashCode是否相等
如果不相等,認為兩個物件也不相等,完畢
如果相等,轉入2)
(這一點只是為了提高儲存效率而要求的,其實理論上沒有也可以,但如果沒有,實際使用時效率會大大降低,所以我們這裡將其做為必需的。後面會重點講到這個問題。)
2),判斷兩個物件用equals運算是否相等
如果不相等,認為兩個物件也不相等
如果相等,認為兩個物件相等(equals()是判斷兩個物件是否相等的關鍵)
為什麼是兩條準則,難道用第一條不行嗎?不行,因為前面已經說了,hashcode()相等時,equals()方法也可能不等,所以必須用第2條準則進行限制,才能保證加入的為非重複元素。
比如下面的程式碼:

public static void main(String args[]){
String s1=new String("zhaoxudong");
String s2=new String("zhaoxudong");
System.out.println(s1==s2);//false
System.out.println(s1.equals(s2));//true
System.out.println(s1.hashCode());//s1.hashcode()等於s2.hashcode()
System.out.println(s2.hashCode());
Set hashset=new HashSet();
hashset.add(s1);
hashset.add(s2);

Iterator it=hashset.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
最後在while迴圈的時候只打印出了一個”zhaoxudong”
輸出結果為:false
true
-967303459
-967303459
這是因為String類已經重寫了equals()方法和hashcode()方法,所以在根據上面的第1.2條原則判定時,hashset認為它們是相等的物件,進行了重複新增。
但是看下面的程式:
import java.util.*;
public class HashSetTest
{
public static void main(String[] args)
{
HashSet hs=new HashSet();
hs.add(new Student(1,"zhangsan"));
hs.add(new Student(2,"lisi"));
hs.add(new Student(3,"wangwu"));
hs.add(new Student(1,"zhangsan"));

Iterator it=hs.iterator();
while(it.hasNext())
{
System.out.println(it.next());
}
}
}
class Student
{
int num;
String name;
Student(int num,String name)
{
this.num=num;
this.name=name;
}
public String toString()
{
return num+":"+name;
}

輸出結果為:
1:zhangsan
1:zhangsan
3:wangwu
2:lisi
問題出現了,為什麼hashset添加了相等的元素呢,這是不是和hashset的原則違背了呢?回答是:沒有
因為在根據hashcode()對兩次建立的new Student(1,"zhangsan")物件進行比較時,生成的是不同的雜湊碼值,所以hashset把他當作不同的物件對待了,當然此時的equals()方法返回的值也不等(這個不用解釋了吧)。那麼為什麼會生成不同的雜湊碼值呢?上面我們在比較s1s2的時候不是生成了同樣的雜湊碼嗎?原因就在於我們自己寫的Student類並沒有重新自己的hashcode()equals()方法,所以在比較時,是繼承的object類中的hashcode()方法,呵呵,各位還記得object類中的hashcode()方法比較的是什麼吧!!
它是一個本地方法,比較的是物件的地址(引用地址),使用new方法建立物件,兩次生成的當然是不同的物件了(這個大家都能理解吧。。。),造成的結果就是兩個物件的hashcode()返回的值不一樣。所以根據第一個準則,hashset會把它們當作不同的物件對待,自然也用不著第二個準則進行判定了。那麼怎麼解決這個問題呢??
答案是:在Student類中重新hashcode()equals()方法。
例如:
class Student
{
int num;
String name;
Student(int num,String name)
{
this.num=num;
this.name=name;
}
public int hashCode()
{
return num*name.hashCode();
}
public boolean equals(Object o)
{
Student s=(Student)o;
return num==s.num && name.equals(s.name);
}
public String toString()
{
return num+":"+name;
}
}
根據重寫的方法,即便兩次呼叫了new Student(1,"zhangsan"),我們在獲得物件的雜湊碼時,根據重寫的方法hashcode(),獲得的雜湊碼肯定是一樣的(這一點應該沒有疑問吧)。
當然根據equals()方法我們也可判斷是相同的。所以在向hashset集合中新增時把它們當作重複元素看待了。所以執行修改後的程式時,我們會發現執行結果是:
1:zhangsan
3:wangwu
2:lisi
可以看到重複元素的問題已經消除。
關於在hibernatepojo類中,重新equals()hashcode()的問題:
1),重點是equals,重寫hashCode只是技術要求(為了提高效率)
2),為什麼要重寫equals呢,因為在java的集合框架中,是通過equals來判斷兩個物件是否相等的
3),在hibernate中,經常使用set集合來儲存相關物件,而set集合是不允許重複的。我們再來談談前面提到在向hashset集合中新增元素時,怎樣判斷物件是否相同的準則,前面說了兩條,其實只要重寫equals()這一條也可以。
但當hashset中元素比較多時,或者是重寫的equals()方法比較複雜時,我們只用equals()方法進行比較判斷,效率也會非常低,所以引入了hashcode()這個方法,只是為了提高效率,但是我覺得這是非常有必要的(所以我們在前面以兩條準則來進行hashset的元素是否重複的判斷)。
比如可以這樣寫:
public int hashCode(){
return 1;}//等價於hashcode無效
這樣做的效果就是在比較雜湊碼的時候不能進行判斷,因為每個物件返回的雜湊碼都是1,每次都必須要經過比較equals()方法後才能進行判斷是否重複,這當然會引起效率的大大降低。
我有一個問題,如果像前面提到的在hashset中判斷元素是否重複的必要方法是equals()方法(根據網上找到的觀點),但是這裡並沒有涉及到關於雜湊表的問題,可是這個集合卻叫hashset,這是為什麼??
我想,在hashmap,hashtable中的儲存操作,依然遵守上面的準則。所以這裡不再多說。這些是今天看書,網上查詢資料,自己總結出來的,部分程式碼和語言是引述,但是千真萬確是自己總結出來的。有錯誤之處和不詳細不清楚的地方還請大家指出,我也是初學者,所以難免會有錯誤的地方,希望大家共同討論。 

建立Student類,重寫equalstoStringhashCode方法,哈有子類的運用。

/*完成下面父類、子類的宣告
 A:宣告Student類,屬性包括:姓名name,學號id,英語成績englishscore,數學成績mathscore,計算機成績computescore,總成績
 方法包括:構造方法,get方法,set方法,tostring方法,equals方法,compare方法(比較兩位學生的總成績,結果大於,小於,等於),sum方法(計算總成績)testAvgScore方法(計算平均成績)

B:宣告StudentXWstudent的子類,並增加責任屬性,重寫testAvgScore方法(計算平均成績=計算平均成績+3
C:宣告StudentBZstudent的子類,,並增加責任屬性,重寫testAvgScore方法(計算平均成績=計算平均成績+5
D:宣告測試類,生成studentstudentxwstudentbz的物件,並分別計算他們的成績*/

//*********************************************************************

1.首先建立Student這個父類如下:

package demo4;

public class Student {
 public int id; //學號
 public String name; //姓名
 public double englishscore; //英語成績
 public double mathscore; //數學成績
 public double computescore; //計算機成績
 public double totalscore; //總成績


 public Student() { //student的無參建構函式
 super();
 }

 public Student(int id, String name, double englishscore,
 double mathscore, double computescore) {//student有參建構函式
 super();
 this.id = id;
 this.name = name;
 this.englishscore = englishscore;
 this.mathscore = mathscore;
 this.computescore = computescore;
 }

 public int getId() {
 return id;
 }
 public void setId(int id) {
 this.id = id;
 }
 public String getName() {
 return name;
 }
 public void setName(String name) {
 this.name = name;
 }
 public double getEnglishscore() {
 return englishscore;
 }
 public void setEnglishscore(double englishscore) {
 this.englishscore = englishscore;
 }
 public double getMathscore() {
 return mathscore;
 }
 public void setMathscore(double mathscore) {
 this.mathscore = mathscore;
 }
 public double getComputescore() {
 return computescore;
 }
 public void setComputescore(double computescore) {
 this.computescore = computescore;
 }
 public double getTotalscore() {
 return totalscore;
 }
 public void setTotalscore(double totalscore) {
 this.totalscore = totalscore;
 }

 public void sum(){ //計算總成績的sum方法
 totalscore=englishscore+mathscore+computescore;
 System.out.println("總成績是:"+totalscore);
 }

 public void compare(Student s2){ //比較兩個學生的總成績方法compare
 if(this.getTotalscore()==s2.getTotalscore()){ //總成績相等的情況
 System.out.println("學生"+this.getName()+"的總成績等於學生"+s2.getName()+"的總成績");
 }else if(this.getTotalscore()>s2.getTotalscore()){//總成績大於的情況
 System.out.println("學生"+this.getName()+"的總成績"+"大於學生"+s2.getName()+"的總成績");
 }else{ //總成績小於的情況
 System.out.println("學生"+this.getName()+"的總成績小於學生"+s2.getName()+"的總成績");
 }
 }

 public void testAvgScore(){ //計算學生的平均成績
 double avgscore=(englishscore+mathscore+computescore)/3;
 System.out.println("平均成績是:"+avgscore);
 }
 @Override
 public String toString(){ //重寫toString的方法
 return "\n學號:"+id+"\n姓名:"+name+"\n英語成績:"+englishscore+"\n數學成績:"
 +mathscore+"\n計算機成績:"+computescore;
 }
 @Override
 public boolean equals(Object o){//重寫equals的方法
 if(o == null) return false;
 if(this.getClass()==o.getClass()){
 Student s=(Student)o;
 System.out.println(this.id+"=="+s.id);
 return s.id==this.id;
 }
 return false;

 }
 @Override
 public int hashCode(){//重寫hsahCode方法
 System.out.println("call hashCode");
 int type=this.getClass().hashCode();
 return type*31+id;
 }


}

//*************************************************************

2.建立子類StudentXW

package demo4;

public class StudentXW extends Student{//StudentXWstudent的子類
 private String liability;

 public StudentXW() { //studentXW的無參建構函式
 super();
 }

 public StudentXW(int id, String name, double englishscore,
 double mathscore, double computescore) {//studentXW有參建構函式
 super();
 this.id = id;
 this.name = name;
 this.englishscore = englishscore;
 this.mathscore = mathscore;
 this.computescore = computescore;
 }

 public void testAvgScore(){ //重寫父類Student中的testAvgScore方法
 super.testAvgScore(); //呼叫父類的testAvgScore方法
 double avgscore=(englishscore+mathscore+computescore)/3+3;
 System.out.println("重寫後平均成績是:"+avgscore);
 }
}
//******************************************

3.建立子類StudentBZ

package demo4;

public class StudentBZ extends Student{//StudentBZstudent的子類
 private String liablity;

 public StudentBZ() { //studentBZ的無參建構函式
 super();
 }

 public StudentBZ(int id, String name, double englishscore,
 double mathscore, double computescore) {//studentBZ有參建構函式
 super();
 this.id = id;
 this.name = name;
 this.englishscore = englishscore;
 this.mathscore = mathscore;
 this.computescore = computescore;
 }

 public void testAvgScore(){ //重寫父類Student中的testAvgScore方法
 super.testAvgScore(); //呼叫父類的testAvgScore方法
 double avgscore=(englishscore+mathscore+computescore)/3+5;
 System.out.println("重寫後平均成績是:"+avgscore);
 }
}

//************************************************************************************************

4.建立測試類TestStudent

package demo4;

import java.util.HashSet;
import java.util.Set;

public class TestStudent {

public static void main(String[] args) {

 Set<Student> set=new HashSet<Student>(); //建立一個集合set,無序的且不重複的。
 Student s1=new Student(1,"lili",85,79,89); //建立student物件s1
 Student s2=new Student(2,"dos",75,79,81); //建立student物件s2
 StudentXW sxw1=new StudentXW(03,"james",80,69.5,90); //建立studentXW物件sxw1
 StudentXW sxw2=new StudentXW(04,"rose",70,64,97); //建立studentXW物件sxw2
 StudentBZ sbz1=new StudentBZ(05,"fly",70,69,80); //建立studentBZ物件sbz1

 //把全部學生新增到set中去
 set.add(s1);
 set.add(s2);
 set.add(sxw1);
 set.add(sxw2);
 set.add(sbz1);

 System.out.println(s1.getName()+"的個人資訊:");
 System.out.println(s1); //輸出重寫toString學生s1的資訊,
 s1.testAvgScore(); //輸出學生s1的平均分
 s1.sum(); //輸出學生s1的總成績
 System.out.println();

 System.out.println(s2.getName()+"的個人資訊:");
 System.out.println(s2); //輸出重寫toString學生s2的資訊,
 s2.testAvgScore(); //輸出學生s2的平均分
 s2.sum(); //輸出學生s2的總成績

 s1.compare(s2); //比較學生s1s2的總成績
 System.out.println();


 System.out.println(sxw1.getName()+"的個人資訊:");
 System.out.println(sxw1); //輸出重寫toString學生sxw1的資訊,
 sxw1.testAvgScore(); //輸出學生sxw1的平均分
 sxw1.sum(); //輸出學生sxw1的總成績
 System.out.println();

 System.out.println(sxw2.getName()+"的個人資訊:");
 System.out.println(sxw2); //輸出重寫toString學生sxw2的資訊,
 sxw2.testAvgScore(); //輸出學生sxw2的平均分
 sxw2.sum(); //輸出學生sxw2的總成績

 sxw1.compare(sxw2); //比較學生s1sxw2的總成績
 System.out.println();


 System.out.println(sbz1.getName()+"的個人資訊:");
 System.out.println(sbz1); //輸出重寫toString學生sbz1的資訊,
 sbz1.testAvgScore(); //輸出學生sbz1的平均分
 sbz1.sum(); //輸出學生sbz1的總成績

 sbz1.compare(sbz1); //比較學生sbz1sbz1的總成績
 System.out.println();

 System.out.println(set); //按集合的方式輸出

}

}

2.Iterator 遍歷集合的方式

import java.util.HashMap;

import java.util.Iterator;

import java.util.Map;

import java.util.Set;

public class MapTest2 {

public static void main(String[] args) {

Map<String,String> map = new HashMap<String,String>();

map.put("1", "中國");

map.put("2", "美國");

map.put("3", "日本");

map.put("4", "德國");

Set enty=map.entrySet();

   Iterator it=enty.iterator();

  while(it.hasNext()){

Map.Entry en=(Map.Entry)it.next();

  System.out.println(en.getKey()+"="+en.getValue());

  }

for(Map.Entry<String,String> entry:map.entrySet()){

System.out.print(entry.getKey()+ "="+entry.getValue());

}

Set keyset = map.keySet();

Iterator it = keyset.iterator();

while (it.hasNext()) {

String s = (String) it.next();

System.out.println(s + "=" + map.get(s));

}

}

}