一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|編程技術(shù)|正則表達式|

服務(wù)器之家 - 編程語言 - JAVA教程 - Java 中HashCode作用_動力節(jié)點Java學院整理

Java 中HashCode作用_動力節(jié)點Java學院整理

2020-09-28 10:41動力節(jié)點 JAVA教程

這篇文章主要介紹了Java 中HashCode作用以及hashcode對于一個對象的重要性,對java中hashcode的作用相關(guān)知識感興趣的朋友一起學習吧

第1 部分 hashCode的作用

  Java集合中有兩類,一類是List,一類是Set他們之間的區(qū)別就在于List集合中的元素師有序的,且可以重復(fù),而Set集合中元素是無序不可重復(fù)的。對于List好處理,但是對于Set而言我們要如何來保證元素不重復(fù)呢?通過迭代來equals()是否相等。數(shù)據(jù)量小還可以接受,當我們的數(shù)據(jù)量大的時候效率可想而知(當然我們可以利用算法進行優(yōu)化)。比如我們向HashSet插入1000數(shù)據(jù),難道我們真的要迭代1000次,調(diào)用1000次equals()方法嗎?hashCode提供了解決方案。怎么實現(xiàn)?我們先看hashCode的源碼(Object)。

public native int hashCode();

  它是一個本地方法,它的實現(xiàn)與本地機器有關(guān),這里我們暫且認為他返回的是對象存儲的物理位置(實際上不是,這里寫是便于理解)。當我們向一個集合中添加某個元素,集合會首先調(diào)用hashCode方法,這樣就可以直接定位它所存儲的位置,若該處沒有其他元素,則直接保存。若該處已經(jīng)有元素存在,就調(diào)用equals方法來匹配這兩個元素是否相同,相同則不存,不同則散列到其他位置。這樣處理,當我們存入大量元素時就可以大大減少調(diào)用equals()方法的次數(shù),極大地提高了效率。

  所以hashCode在上面扮演的角色為尋域(尋找某個對象在集合中區(qū)域位置)。hashCode可以將集合分成若干個區(qū)域,每個對象都可以計算出他們的hash碼,可以將hash碼分組,每個分組對應(yīng)著某個存儲區(qū)域,根據(jù)一個對象的hash碼就可以確定該對象所存儲區(qū)域,這樣就大大減少查詢匹配元素的數(shù)量,提高了查詢效率。 

  如何理解hashCode的作用:

  以java.lang.Object來理解,JVM每new一個Object,它都會將這個Object丟到一個Hash哈希表中去,這樣的話,下次做Object的比較或者取這個對象的時候,它會根據(jù)對象的hashcode再從Hash表中取這個對象。這樣做的目的是提高取對象的效率。具體過程是這樣:

1.new Object(),JVM根據(jù)這個對象的Hashcode值,放入到對應(yīng)的Hash表對應(yīng)的Key上,如果不同的對象確產(chǎn)生了相同的hash值,也就是發(fā)生了Hash key相同導(dǎo)致沖突的情況,那么就在這個Hash key的地方產(chǎn)生一個鏈表,將所有產(chǎn)生相同hashcode的對象放到這個單鏈表上去,串在一起。

2.比較兩個對象的時候,首先根據(jù)他們的hashcode去hash表中找他的對象,當兩個對象的hashcode相同,那么就是說他們這兩個對象放在Hash表中的同一個key上,那么他們一定在這個key上的鏈表上。那么此時就只能根據(jù)Object的equal方法來比較這個對象是否equal。當兩個對象的hashcode不同的話,肯定他們不能equal.

java.lang.Object中對hashCode的約定:

   1. 在一個應(yīng)用程序執(zhí)行期間,如果一個對象的equals方法做比較所用到的信息沒有被修改的話,則對該對象調(diào)用hashCode方法多次,它必須始終如一地返回同一個整數(shù)。

   2. 如果兩個對象根據(jù)equals(Object o)方法是相等的,則調(diào)用這兩個對象中任一對象的hashCode方法必須產(chǎn)生相同的整數(shù)結(jié)果。
 

  3. 如果兩個對象根據(jù)equals(Object o)方法是不相等的,則調(diào)用這兩個對象中任一個對象的hashCode方法,不要求產(chǎn)生不同的整數(shù)結(jié)果。但如果能不同,則可能提高散列表的性能。

  有一個概念要牢記,兩個相等對象的equals方法一定為true, 但兩個hashcode相等的對象不一定是相等的對象。
所以hashcode相等只能保證兩個對象在一個HASH表里的同一條HASH鏈上,繼而通過equals方法才能確定是不是同一對象,如果結(jié)果為true, 則認為是同一對象在插入,否則認為是不同對象繼續(xù)插入。 

第2部分 hashCode對于一個對象的重要性

  一個對象的HashCode就是一個簡單的Hash算法的實現(xiàn),雖然它和那些真正的復(fù)雜的Hash算法相比還不能叫真正的算法,它如何實現(xiàn)它,不僅僅是程序員的編程水平問題, 而是關(guān)系到你的對象在存取是性能的非常重要的關(guān)系.有可能,不同的HashCode可能會使你的對象存取產(chǎn)生,成百上千倍的性能差別。先來看一下,在JAVA中兩個重要的數(shù)據(jù)結(jié)構(gòu):HashMap和Hashtable,雖然它們有很大的區(qū)別,如繼承關(guān)系不同,對value的約束條件(是否允許null)不同,以及線程安全性等有著特定的區(qū)別,但從實現(xiàn)原理上來說,它們是一致的。所以,只以Hashtable來說明:

  在java中,存取數(shù)據(jù)的性能,一般來說當然是首推數(shù)組,但是在數(shù)據(jù)量稍大的容器選擇中,Hashtable將有比數(shù)據(jù)性能更高的查詢速度。具體原因看下面的內(nèi)容,Hashtable在存儲數(shù)據(jù)時,一般先將該對象的HashCode和0x7FFFFFFF做與操作,因為一個對象的HashCode可以為負數(shù),這樣操作后可以保證它為一個正整數(shù)。然后以Hashtable的長度取模,得到該對象在Hashtable中的索引。

int index = (hash & 0x7FFFFFFF) % tab.length;

這個對象就會直接放在Hashtable的每index位置,對于寫入,這和數(shù)據(jù)一樣,把一個對象放在其中的第index位置,但如果是查詢,經(jīng)過同樣的算法,Hashtable可以直接從第index取得這個對象,而數(shù)組卻要做循環(huán)比較.所以對于數(shù)據(jù)量稍大時,Hashtable的查詢比數(shù)據(jù)具有更高的性能。

既然一個對象可以根據(jù)HashCode直接定位它在Hashtable中的位置,那么為什么Hashtable還要用key來做映射呢?這就是關(guān)系Hashtable性能問題的最重要的問題:Hash沖突。

  常見的Hash沖突是不同對象最終產(chǎn)生了相同的索引,而一種非常甚至絕對少見的Hash沖突是,如果一組對象的個數(shù)大過了int 范圍,而HashCode的長度只能在int范圍中,所以肯定要有同一組的元素有相同的HashCode,這樣無論如何他們都會有相同的 索引。當然這種極端的情況是極少見的,可以暫不考慮,但是對于同的HashCode經(jīng)過取模,則會產(chǎn)生相同的索引,或者不同 的對象卻具有相同的HashCode,當然具有相同的索引。所以對于索引相同的對象,在該index位置存放了多個值,這些值要想能正確區(qū)分,就要依靠key來識別。事實上一個設(shè)計各好的HashTable,一般來說會比較平均地分布每個元素,因為Hashtable的長度總是比實際元素的個數(shù)按一 定比例進行自增(裝填因子一般為0.75)左右,這樣大多數(shù)的索引位置只有一個對象,而很少的位置會有幾個元素,所以 Hashtable中的每個位置存放的是一個鏈表,對于只有一個對象是位置,鏈表只有一個首節(jié)點(Entry),Entry的next為null。然后有hashCode,key,value屬性保存了該位置的對象的HashCode,key和value(對象本身),如果有相同索引的對象進來則 會進入鏈表的下一個節(jié)點。如果同一個索引中有多個對象,根據(jù)HashCode和key可以在該鏈表中找到一個和查詢的key相匹配的對象。

  從上面我看可以看到,對于HashMap和Hashtable的存取性能有重大影響的首先是應(yīng)該使該數(shù)據(jù)結(jié)構(gòu)中的元素盡量大可能具有不同的HashCode,雖然這并不能保證不同的HashCode產(chǎn)生不同的index,但相同的HashCode一定產(chǎn)生相同的index,從而影響 產(chǎn)生Hash沖突。

對于一個象,如果具有很多屬性,把所有屬性都參與散列,顯然是一種笨拙的設(shè)計。因為對象的HashCode()方法幾乎無所不在 地被自動調(diào)用,如equals比較,如果太多的對象參與了散列。那么需要的操作常數(shù)時間將會增加很大。所以,挑選哪些屬性參與散列絕對是一個編程水平的問題。

我們知道,每次調(diào)用HashCode方法方法,都要重新 對方法內(nèi)的參與散列的對象重新計算一次它們的HashCode的運算,如果一個對象的屬性沒有改變,仍然要每次都進行計算, 所以如果設(shè)置一個標記來緩存當前的散列碼,只要當參與散列的對象改變時才重新計算,否則調(diào)用緩存的hashCode,這可以 從很大程度上提高性能。默認的實現(xiàn)是將對象內(nèi)部地址轉(zhuǎn)化為整數(shù)作為HashCode,這當然能保證每個對象具有不同的HasCode,因為不同的對象內(nèi)部地 址肯定不同,但java語言并不能讓程序員獲取對象內(nèi)部地址,所以,讓每個對象產(chǎn)生不同的HashCode有著很多可研究 的技術(shù)。如果從多個屬性中采樣出能具有平均分布的hashCode的屬性,這是一個性能和多樣性相矛盾的地方,如果所有屬性都參與散 列,當然hashCode的多樣性將大大提高,但犧牲了性能,而如果只能少量的屬性采樣散列,極端情況會產(chǎn)生大量的散列沖突 ,如對"人"的屬性中,如果用性別而不是姓名或出生日期,那將只有兩個或幾個可選的hashcode值,將產(chǎn)生一半以上的散列 沖突.所以如果可能的條件下,專門產(chǎn)生一個序列用來生成HashCode將是一個好的選擇(當然產(chǎn)生序列的性能要比所有屬性參 與散列的性能高的情況下才行,否則還不如直接用所有屬性散列). 如何對HashCode的性能和多樣性求得一個平衡,可以參考相關(guān)算法設(shè)計的書,其實并不一定要求非常的優(yōu)秀,只要能盡最大 可能減少散列值的聚集。重要的是我們應(yīng)該記得HashCode對于我們的程序性能有著重要的影響,在程序設(shè)計時應(yīng)該時時加以注 意.

請記住:如果你想有效的使用HashMap,你就必須重寫在其的HashCode()。

還有兩條重寫HashCode()的原則:

1.不必對每個不同的對象都產(chǎn)生一個唯一的hashcode,只要你的HashCode方法使get()能夠得到put()放進去的內(nèi)容就可以了。即“不為一原則”。

2.生成hashcode的算法盡量使hashcode的值分散一些, 不要很多hashcode都集中在一個范圍內(nèi),這樣有利 于提高HashMap的性能。即“分散原則”。

至于第二條原則的具體原因,有興趣者可以參考Bruce Eckel的《Thinking in Java》,在那里有對HashMap內(nèi)部實現(xiàn)原理的介紹,這里就不贅述了。 

第3部分 hashCode與equals

在Java中hashCode的實現(xiàn)總是伴隨著equals,他們是緊密配合的,你要是自己設(shè)計了其中一個,就要設(shè)計另外一個。當然在多數(shù)情況下,這兩個方法是不用我們考慮的,直接使用默認方法就可以幫助我們解決很多問題。但是在有些情況,我們必須要自己動手來實現(xiàn)它,才能確保程序更好的運作。

      對于equals,我們必須遵循如下規(guī)則:

      對稱性:如果x.equals(y)返回是“true”,那么y.equals(x)也應(yīng)該返回是“true”。

      反射性:x.equals(x)必須返回是“true”。

      類推性:如果x.equals(y)返回是“true”,而且y.equals(z)返回是“true”,那么z.equals(x)也應(yīng)該返回是“true”。

      一致性:如果x.equals(y)返回是“true”,只要x和y內(nèi)容一直不變,不管你重復(fù)x.equals(y)多少次,返回都是“true”。

      任何情況下,x.equals(null),永遠返回是“false”;x.equals(和x不同類型的對象)永遠返回是“false”。

      對于hashCode,我們應(yīng)該遵循如下規(guī)則:

      1. 在一個應(yīng)用程序執(zhí)行期間,如果一個對象的equals方法做比較所用到的信息沒有被修改的話,則對該對象調(diào)用hashCode方法多次,它必須始終如一地返回同一個整數(shù)。   

   2. 如果兩個對象根據(jù)equals(Object o)方法是相等的,則調(diào)用這兩個對象中任一對象的hashCode方法必須產(chǎn)生相同的整數(shù)結(jié)果。

      3. 如果兩個對象根據(jù)equals(Object o)方法是不相等的,則調(diào)用這兩個對象中任一個對象的hashCode方法,不要求產(chǎn)生不同的整數(shù)結(jié)果。但如果能不同,則可能提高散列表的性能。

      至于兩者之間的關(guān)聯(lián)關(guān)系,我們只需要記住如下即可:

      如果x.equals(y)返回“true”,那么x和y的hashCode()必須相等。

      如果x.equals(y)返回“false”,那么x和y的hashCode()有可能相等,也有可能不等。

      理清了上面的關(guān)系我們就知道他們兩者是如何配合起來工作的。先看下圖: 

Java 中HashCode作用_動力節(jié)點Java學院整理

      整個處理流程是:

      1、判斷兩個對象的hashcode是否相等,若不等,則認為兩個對象不等,完畢,若相等,則比較equals。

      2、若兩個對象的equals不等,則可以認為兩個對象不等,否則認為他們相等。

實例: 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package com.weakHashMap;
/**
* 重新實現(xiàn)了hashCode方法和equals方法
* @ClassName: Person
*
*
*/
public class Person {
 private int age;
 private int sex; // 0:男,1:女
 private String name;
 private final int PRIME = 37;
 Person(int age ,int sex ,String name){
  this.age = age;
  this.sex = sex;
  this.name = name;
 }
 public int getAge() {
  return age;
 }
 public void setAge(int age) {
  this.age = age;
 }
 public int getSex() {
  return sex;
 }
 public void setSex(int sex) {
  this.sex = sex;
 }
 public String getName() {
  return name;
 }
 public void setName(String name) {
  this.name = name;
 }
 @Override
 public int hashCode() {
  System.out.println("調(diào)用hashCode方法...........");
  int hashResult = 1;
  hashResult = (hashResult + Integer.valueOf(age).hashCode() + Integer.valueOf(sex).hashCode()) * PRIME;
  hashResult = PRIME * hashResult + ((name == null) ? 0 : name.hashCode());
  System.out.println("name:"+name +" hashCode:" + hashResult);
  return hashResult;
 }
 /**
 * 重寫hashCode()
 */
 public boolean equals(Object obj) {
  System.out.println("調(diào)用equals方法...........");
  if(obj == null){
   return false;
  }
  if(obj.getClass() != this.getClass()){
   return false;
  }
  if(this == obj){
   return true;
  }
  Person person = (Person) obj;
  if(getAge() != person.getAge() || getSex()!= person.getSex()){
   return false;
  }
  if(getName() != null){
   if(!getName().equals(person.getName())){
    return false;
   }
  }
  else if(person != null){
   return false;
  }
  return true;
 }
 
}
 
package com.weakHashMap;
import java.util.HashSet;
import java.util.Set;
/**
*
* @ClassName: test1
* TODO *
*
*/
public class test extends Person{
 /**
 * @param age
 * @param sex
 * @param name
 */
 test(int age, int sex, String name) {
  super(age, sex, name);
  // TODO Auto-generated constructor stub
 }
 public static void main(String[] args){
  Set<Person> set = new HashSet<Person>();
  Person p1 = new Person(11, 1, "張三");
  Person p2 = new Person(12, 1, "李四");
 Person p3 = new Person(11, 1, "張三");
  Person p4 = new Person(11, 1, "李四");
  //只驗證p1、p3
  System.out.println("p1 == p3? :" + (p1 == p3));
 System.out.println("p1.equals(p3)?:"+p1.equals(p3));
  System.out.println("-----------------------分割線--------------------------");
 set.add(p1);
 set.add(p2);
 set.add(p3);
  set.add(p4);
 System.out.println("set.size()="+set.size());
}
}

結(jié)果:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
p1 == p3? :false
調(diào)用equals方法...........
p1.equals(p3)?:true
-----------------------分割線--------------------------
調(diào)用hashCode方法...........
name:張三 hashCode:792686
調(diào)用hashCode方法...........
name:李四 hashCode:861227
調(diào)用hashCode方法...........
name:張三 hashCode:792686
調(diào)用equals方法...........
調(diào)用hashCode方法...........
name:李四 hashCode:859858
set.size()=3

可以看到張三的兩次hashCode相同,調(diào)用equals進行匹配,最后hashCode 和 equals 都返回true,則不添加。

以上所述是小編給大家介紹的Java 中HashCode作用_動力節(jié)點Java學院整理,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對服務(wù)器之家網(wǎng)站的支持!

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美日韩中文字幕一区二区高清 | 俄罗斯bbbbbbbbb大片 | 亚洲日本免费 | 亚洲国产精久久久久久久 | 午夜爽喷水无码成人18禁三级 | 国产大片网站 | 国内自拍网红在线自拍综合 | 问一问免费咨询 | sss在线播放| 爱豆传媒最新视频国产 | 国产精品福利 | 日韩亚洲欧美综合一区二区三区 | 性欧美video| 女张腿男人桶羞羞漫画 | 美女露尿口 | 视频在线网站 | 成版人快猫永久破解版 | 公交车强校花系列小说 | 国产精品嫩草影院在线 | 色综合色狠狠天天综合色 | 女子监狱第二季未删减在线看 | 好 舒服 好 粗 好硬免费视频 | 深夜视频免费看 | 国产日韩欧美在线一二三四 | gay小太正初精 | 欧美亚洲激情在线 | 四虎国产成人亚洲精品 | 欧美一区二区三区视视频 | 四虎www| 亚洲精品视频导航 | 欧美精品日韩 | 日本xxx在线观看免费播放 | 精品综合久久久久久8888 | 好大好想要 | 免费看60分钟大片视频播放 | 动漫美女人物被黄漫在线看 | 男人看的网址 | 国产在线观看a | 丁香六月色婷婷综合网 | 丁香婷婷在线视频 | 日本连裤袜xxxxx在线视频 |