欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

深入理解Java中HashCode方法

 更新時間:2018年01月08日 15:16:13   作者:御風(fēng)逍遙  
這篇文章主要介紹了深入理解Java中HashCode方法,具有一定借鑒價值,需要的朋友可以參考下

關(guān)于hashCode,維基百科中:

In the Java programming language, every class implicitly or explicitly 
provides a hashCode() method, which digests the data stored in an 
instance of the class into a single hash value (a 32-bit signed 
integer).

hashCode就是根據(jù)存儲在一個對象實例中的所有數(shù)據(jù),提取出一個32位的整數(shù),該整數(shù)的目的是用來標(biāo)示該實例的唯一性。有點類似于MD5碼,每個文件都能通過MD5算法生成一個唯一的MD5碼。不過,Java中的hashCode并沒有真正的實現(xiàn)為每個對象生成一個唯一的hashCode,還是會有一定的重復(fù)幾率。

先來看看Object類,我們知道,Object類是java程序中所有類的直接或間接父類,處于類層次的最高點。在Object類里定義了很多我們常見的方法,包括我們要講的hashCode方法,如下

public final native Class<?> getClass(); 
public native int hashCode(); 
public boolean equals(Object obj) { 
 return (this == obj); 
}  
public String toString() { 
 return getClass().getName() + "@" + Integer.toHexString(hashCode()); 
} 

注意到hashCode方法前面有個native的修飾符,這表示hashCode方法是由非java語言實現(xiàn)的,具體的方法實現(xiàn)在外部,返回內(nèi)存對象的地址。

在java的很多類中都會重寫equals和hashCode方法,這是為什么呢?最常見的String類,比如我定義兩個字符相同的字符串,那么對它們進(jìn)行比較時,我想要的結(jié)果應(yīng)該是相等的,如果你不重寫equals和hashCode方法,他們肯定是不會相等的,因為兩個對象的內(nèi)存地址不一樣。

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; 
  } 

其實這段代碼是這個數(shù)學(xué)表達(dá)式的實現(xiàn)

s[0]*31^(n-1) + s[1]*31^(n-2) + … + s[n-1]

s[i]是string的第i個字符,n是String的長度。那為什么這里用31,而不是其它數(shù)呢?《Effective Java》是這樣說的:之所以選擇31,是因為它是個奇素數(shù),如果乘數(shù)是偶數(shù),并且乘法溢出的話,信息就會丟失,因為與2相乘等價于移位運算。使用素數(shù)的好處并不是很明顯,但是習(xí)慣上都使用素數(shù)來計算散列結(jié)果。31有個很好的特性,就是用移位和減法來代替乘法,可以得到更好的性能:31*i==(i<<5)-i。現(xiàn)在的VM可以自動完成這種優(yōu)化。

可以看到,String類是用它的value值作為參數(shù)來計算hashCode的,也就是說,相同的value就一定會有相同的hashCode值。這點也很容易理解,因為value值相同,那么用equals比較也是相等的,equals方法比較相等,則hashCode一定相等。反過來不一定成立。它不保證相同的hashCode一定有相同的對象。

一個好的hash函數(shù)應(yīng)該是這樣的:為不相同的對象產(chǎn)生不相等的hashCode。

在理想情況下,hash函數(shù)應(yīng)該把集合中不相等的實例均勻分布到所有可能的hashCode上,要想達(dá)到這種理想情形是非常困難的,至少java沒有達(dá)到。因為我們可以看到,hashCode是非隨機(jī)生成的,它有一定的規(guī)律,就是上面的數(shù)學(xué)等式,我們可以構(gòu)造一些具有相同hashCode但value值不一樣的,比如說:Aa和BB的hashCode是一樣的。

如下代碼:

public class Main {
  public static void main(String[] args) {
    Main m = new Main();
    System.out.println(m);
    System.out.println(Integer.toHexString(m.hashCode()));
    String a = "Aa";
    String b = "BB";
    System.out.println(a.hashCode());
    System.out.println(b.hashCode());
  }
}

輸出結(jié)果:

Main@2a139a55 
2a139a55 
2112 
2112

一般在重寫equal函數(shù)時,也要重寫hashCode函數(shù),這是為什么呢?

來看看這個例子,讓我們創(chuàng)建一個簡單的類Employee

public class Employee
{
  private Integer id;
  private String firstname;
  private String lastName;
  private String department;

  public Integer getId() {
    return id;
  }
  public void setId(Integer id) {
    this.id = id;
  }
  public String getFirstname() {
    return firstname;
  }
  public void setFirstname(String firstname) {
    this.firstname = firstname;
  }
  public String getLastName() {
    return lastName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public String getDepartment() {
    return department;
  }
  public void setDepartment(String department) {
    this.department = department;
  }
}

上面的Employee類只是有一些非?;A(chǔ)的屬性和getter、setter.現(xiàn)在來考慮一個你需要比較兩個employee的情形。

public class EqualsTest {
  public static void main(String[] args) {
    Employee e1 = new Employee();
    Employee e2 = new Employee();

    e1.setId(100);
    e2.setId(100);
    //Prints false in console
    System.out.println(e1.equals(e2));
  }
}

毫無疑問,上面的程序?qū)⑤敵鰂alse,但是,事實上上面兩個對象代表的是通過一個employee。真正的商業(yè)邏輯希望我們返回true。

為了達(dá)到這個目的,我們需要重寫equals方法。

public boolean equals(Object o) {
    if(o == null)
    {
      return false;
    }
    if (o == this)
    {
      return true;
    }
    if (getClass() != o.getClass())
    {
      return false;
    }
    Employee e = (Employee) o;
    return (this.getId() == e.getId());
}

在上面的類中添加這個方法,EauqlsTest將會輸出true。

So are we done?沒有,讓我們換一種測試方法來看看。

import java.util.HashSet;
import java.util.Set;
public class EqualsTest
{
	public static void main(String[] args)
	  {
		Employee e1 = new Employee();
		Employee e2 = new Employee();
		e1.setId(100);
		e2.setId(100);
		//Prints 'true'
		System.out.println(e1.equals(e2));
		Set<Employee> employees = new HashSet<Employee>();
		employees.add(e1);
		employees.add(e2);
		//Prints two objects
		System.out.println(employees);
	}

上面的程序輸出的結(jié)果是兩個。如果兩個employee對象equals返回true,Set中應(yīng)該只存儲一個對象才對,問題在哪里呢?

我們忘掉了第二個重要的方法hashCode()。就像JDK的Javadoc中所說的一樣,如果重寫equals()方法必須要重寫hashCode()方法。我們加上下面這個方法,程序?qū)?zhí)行正確。

@Override
 public int hashCode()
 {
  final int PRIME = 31;
  int result = 1;
  result = PRIME * result + getId();
  return result;
 }

需要注意記住的事情

盡量保證使用對象的同一個屬性來生成hashCode()和equals()兩個方法。在我們的案例中,我們使用員工id。
eqauls方法必須保證一致(如果對象沒有被修改,equals應(yīng)該返回相同的值)
任何時候只要a.equals(b),那么a.hashCode()必須和b.hashCode()相等。
兩者必須同時重寫。

總結(jié)

以上就是本文關(guān)于深入理解Java中HashCode方法的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)參閱本站其他相關(guān)專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!

相關(guān)文章

  • JDBC獲取元數(shù)據(jù)demo

    JDBC獲取元數(shù)據(jù)demo

    這篇文章主要為大家介紹了JDBC獲取元數(shù)據(jù)實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-11-11
  • JAVA中對List進(jìn)行查詢

    JAVA中對List進(jìn)行查詢

    下面小編就為大家?guī)硪黄趈ava List中進(jìn)行模糊查詢的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-08-08
  • Java的內(nèi)存機(jī)制詳解

    Java的內(nèi)存機(jī)制詳解

    本文主要介紹了Java的內(nèi)存機(jī)制的相關(guān)知識,具有很好的參考價值,下面跟著小編一起來看下吧
    2017-03-03
  • Lombok插件的安裝與簡單使用步驟

    Lombok插件的安裝與簡單使用步驟

    這篇文章主要介紹了Lombok插件的安裝與簡單使用步驟,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • Spring整合SpringMVC + Mybatis基礎(chǔ)框架的配置文件詳解

    Spring整合SpringMVC + Mybatis基礎(chǔ)框架的配置文件詳解

    這篇文章主要介紹了Spring整合SpringMVC + Mybatis基礎(chǔ)框架的配置文件,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-02-02
  • Java?EE實現(xiàn)用戶后臺管理系統(tǒng)

    Java?EE實現(xiàn)用戶后臺管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java?EE實現(xiàn)用戶后臺管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-05-05
  • Java?Spring?AOP源碼解析之事務(wù)實現(xiàn)原理

    Java?Spring?AOP源碼解析之事務(wù)實現(xiàn)原理

    這篇文章主要為大家介紹了Java?Spring?AOP事務(wù)實現(xiàn)原理,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • Springboot實現(xiàn)對配置文件中的明文密碼加密詳解

    Springboot實現(xiàn)對配置文件中的明文密碼加密詳解

    我們在SpringBoot項目當(dāng)中,會把數(shù)據(jù)庫的用戶名密碼等配置直接放在yaml或者properties文件中,這樣維護(hù)數(shù)據(jù)庫的密碼等敏感信息顯然是有一定風(fēng)險的。所以本文為大家整理了對配置文件中的明文密碼加密的方法,希望對大家有所幫助
    2023-03-03
  • Java多線程之間日志traceId傳遞方式

    Java多線程之間日志traceId傳遞方式

    這篇文章主要介紹了Java多線程之間日志traceId傳遞方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08
  • Springboot Mybatis-Plus數(shù)據(jù)庫單元測試實戰(zhàn)(三種方式)

    Springboot Mybatis-Plus數(shù)據(jù)庫單元測試實戰(zhàn)(三種方式)

    這篇文章主要介紹了Springboot Mybatis-Plus數(shù)據(jù)庫單元測試實戰(zhàn)(三種方式),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12

最新評論