亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

詳解Java中的hashcode

 更新時(shí)間:2021年05月07日 16:16:45   作者:客官不愛(ài)喝酒  
這篇文章主要介紹了詳解Java中的hashcode,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下

一、什么是hash

Hash,一般翻譯做散列、雜湊,或音譯為哈希,是把任意長(zhǎng)度的輸入(又叫做預(yù)映射pre-image)通過(guò)散列算法變換成固定長(zhǎng)度的輸出,該輸出就是散列值。這種轉(zhuǎn)換是一種壓縮映射,也就是,散列值的空間通常遠(yuǎn)小于輸入的空間,不同的輸入可能會(huì)散列成相同的輸出,所以不可能從散列值來(lái)確定唯一的輸入值。簡(jiǎn)單的說(shuō)就是一種將任意長(zhǎng)度的消息壓縮到某一固定長(zhǎng)度的消息摘要的函數(shù)。

這個(gè)說(shuō)的有點(diǎn)官方,你就可以把它簡(jiǎn)單的理解為一個(gè)key,就像是map的key值一樣,是不可重復(fù)的。

二、hash有什么用?,在什么地方用到?

1.在散列表

2.map集合

此處只是做了一個(gè)簡(jiǎn)單的介紹,其實(shí)遠(yuǎn)遠(yuǎn)沒(méi)有那么簡(jiǎn)單,如算法里面的散列表,散列函數(shù),以及map的一個(gè)不可重復(fù)到底是怎么樣去判斷的,以及hash沖突的問(wèn)題,都與hash值離不開(kāi)關(guān)系。

三、java中String類的hashcode方法

public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

可以看到,String類的hashcode方法是通過(guò),char類型的方式進(jìn)行一個(gè)相加,因?yàn)镾tring類的底層就是通過(guò)char數(shù)組來(lái)實(shí)現(xiàn)的。

如:String str="ab"; 其實(shí)就是一個(gè)char數(shù)組 char[] str={'a','b'};

如:字符串 String star=“ab”;那么他對(duì)應(yīng)的hash值就是:3105

怎么算出來(lái)的呢?

val[0]='a' ; val[1]='b'

a對(duì)應(yīng)的Ascall碼值為:97
b對(duì)的Ascall碼值為:98
那么經(jīng)過(guò)如下代碼的循環(huán)相加

 for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }

得出:97*31+98=3105;

我們?cè)倏矗?int h = hash;這一個(gè)代碼,為什么有時(shí)候調(diào)用hashcode方法返回的hash值是負(fù)數(shù)呢?

因?yàn)槿绻@個(gè)字符串很長(zhǎng)?那么h的值就會(huì)超過(guò)int類型的最大值,有沒(méi)有想過(guò),如果一個(gè)int類型的最大值,超過(guò)他的范圍之后會(huì)怎么樣呢?

我們先來(lái)看這樣一個(gè)簡(jiǎn)單的代碼:

 int count=0;
    while (true){
   if (count++<10){
            System.out.println("hello world");
          
        }
    }

大家認(rèn)為hello world會(huì)輸出多少次呢?
正常情況來(lái)說(shuō)count小于10就不會(huì)輸出了對(duì)么?
但是其實(shí)并不是這樣的,很明確的告訴大家,這是一個(gè)死循環(huán)。

因?yàn)閏ount一直加,最開(kāi)始if成立,到后面的if不成立,再到后面if會(huì)再次成立,為什么會(huì)成立呢?因?yàn)閏ount變?yōu)榱素?fù)數(shù)。

???? 為什么?因?yàn)閏ount一直無(wú)限制的加,由于是線性增長(zhǎng),計(jì)算速度是非??斓?,所以,要不了多久,就會(huì)超出int類型的最大值??刂戚敵龅腸ount變?yōu)榱素?fù)數(shù),所以呢此時(shí)的if條件又成立了。

在這里插入圖片描述

首先我們來(lái)看下int的范圍 :int 為4個(gè)字節(jié),一個(gè)字節(jié)為8位,一個(gè)int值是需要的存儲(chǔ)空間是32位,但是呢?這是一個(gè)有符號(hào)位的int,需要一個(gè)符號(hào)位來(lái)表示正數(shù)和負(fù)數(shù),
int值的范圍就是:-2^31 ~ 2^31 也就是:-2147483648 到2147483647

我們來(lái)看下int最大值對(duì)應(yīng)的二進(jìn)制位是對(duì)少?

在這里插入圖片描述

全是1? 2147483647最大值不是一個(gè)正數(shù)么?難道第一位難道不是應(yīng)該用0表示么?

此時(shí)這個(gè)0他是省略掉了沒(méi)寫(xiě)的,由于二進(jìn)制系統(tǒng)是通過(guò)補(bǔ)碼來(lái)保存數(shù)據(jù)的。第一位是符號(hào)位,0為正,1為負(fù),當(dāng)正的除了符號(hào)位全為1,再加1就進(jìn)位了,符號(hào)位就會(huì)變成1,是負(fù)數(shù),其他為0。
所以說(shuō)當(dāng)int的最大值加一個(gè)1,就變?yōu)榱?,最小?/p>

來(lái)!口說(shuō)無(wú)憑,沒(méi)有說(shuō)服力,我們直接來(lái)看代碼:

在這里插入圖片描述

那么我們?cè)诜催^(guò)來(lái)想,最小值減1呢?那是不是就是對(duì)對(duì)應(yīng)的是我們int類型的最大值了呀?

認(rèn)真仔細(xì)的看完這個(gè),你就知道為什么hashcode方法調(diào)用的時(shí)候有時(shí)候是正數(shù),有時(shí)候是負(fù)數(shù)了吧?所以說(shuō)底層還是很有意思的,比如往后做大數(shù)據(jù)開(kāi)發(fā),那么肯定是需要深入理解這些數(shù)據(jù)類型的范圍,以及他的一個(gè)變化規(guī)則,否則有時(shí)候不知不覺(jué)的就犯錯(cuò)了,你還不知道錯(cuò)誤在那兒?嚴(yán)重的話就是會(huì)損失精度,導(dǎo)致結(jié)果和你預(yù)期的結(jié)果相差甚遠(yuǎn)。就因?yàn)榧恿藗€(gè)1,就導(dǎo)致結(jié)果和預(yù)期結(jié)果相差十萬(wàn)八千里。

這是String類的hashcode方法的一個(gè)具體實(shí)現(xiàn)過(guò)程。

四、兩個(gè)對(duì)象的 hashCode()相同,則 equals()也一定為 true,對(duì)嗎?

首先呢?這個(gè)肯定是不對(duì)的。

因?yàn)檫@兩個(gè)方法都可以重寫(xiě),如果是自己的類,那么可以靈活重寫(xiě),如果是jdk自己的類,那就要看他到底有沒(méi)有重寫(xiě)這兩個(gè)方法。

我們以String類的equlas方法為例:我們都知道,如果equlas方法如果沒(méi)有重寫(xiě),那就繼承Object類的equlas方法,默認(rèn)比較的是兩個(gè)對(duì)象的內(nèi)存地址,如果重寫(xiě)了,那就根據(jù)我們自己重寫(xiě)的規(guī)則來(lái)比較兩個(gè)對(duì)象是否相等。

如下是jdkString類中自己重寫(xiě)的equals方法,

 public boolean equals(Object anObject) {
    // 如果兩個(gè)String類型的對(duì)象內(nèi)存地址相等直接返回true
        if (this == anObject) {
            return true;
        }
       // 如下做的操作就是比較兩個(gè)字符串中的每一個(gè)char類型的數(shù)據(jù),如果都完全匹配,那么就是返回true,如果有一個(gè)不相同,直接返回false,并且兩個(gè)字符串的長(zhǎng)度要相等,如果不相同,那么肯定也就不可能值一樣了嘛
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

再看下String的hashcode方法:就是我們上面剛剛講過(guò)的那個(gè)方法

 public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;

            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];
            }
            hash = h;
        }
        return h;
    }

1:如果兩個(gè)String類型的對(duì)象內(nèi)存地址相等直接返回true

2:比較兩個(gè)字符串中的每一個(gè)char類型的數(shù)據(jù),如果都完全匹配,那么就是返回true,如果有一個(gè)不相同,直接返回false,并且兩個(gè)字符串的長(zhǎng)度要相等,如果不相同,那么肯定也就不可能值一樣了嘛

由此可以看出,equals方法是否返回true跟hashcode方法沒(méi)有半毛錢(qián)關(guān)系嘛。

接下來(lái)我們看下,我們自定義的對(duì)象,我創(chuàng)建一個(gè)User類,里面有屬性id和name

public class User {
    int id;
    String name;

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }


}

在沒(méi)有重寫(xiě)hashcode方法和equals方法的情況下進(jìn)行比較:

 User user1 = new User();
        User user2 = new User();

        System.out.println(user1.hashCode());
        System.out.println(user2.hashCode());
        System.out.println(user1.equals(user2));

在這里插入圖片描述

解答:因?yàn)槲覀儧](méi)有重寫(xiě),這兩個(gè)方法都是繼承自O(shè)bject類的,所以呢?比較的是內(nèi)存地址,因?yàn)槲覀兪峭ㄟ^(guò)new的方式去創(chuàng)建的兩個(gè)user對(duì)象,那么他們的內(nèi)存地址肯定是不相同的,所以直接返回false,而hashcode方法是java的底層語(yǔ)言c++來(lái)寫(xiě)的,具體他內(nèi)部是怎么實(shí)現(xiàn)的,封裝了,我也就不得而知了,后面再了解,有的人說(shuō)是跟內(nèi)存地址相關(guān),但是具體我沒(méi)有看到具體實(shí)現(xiàn),也不敢茍同,有的東西,需要自己不斷的去摸索,自己親自實(shí)踐,才是真的,不要相信別人說(shuō)的。

好的,接下來(lái)我們重寫(xiě)User類的hashcode方法:

public class User {
    int id;
    String name;



    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }



    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

在這里插入圖片描述

可以看到的是,我們兩個(gè)User對(duì)象的hashcode方法返回的hash值是完全一致的,但是通過(guò)equals方法進(jìn)行比較,還是false,所以說(shuō),兩個(gè)對(duì)象的hashcode方法返回的hash值相同,equlas也一定返回true是完全不成立的,直接推翻。

接下來(lái)我們?cè)俅沃貙?xiě)equlas方法,此時(shí)我們規(guī)定,只要兩個(gè)對(duì)象的id和name都相同,那么這兩個(gè)對(duì)象就是同一個(gè)對(duì)象。并且刪除掉剛剛我們重寫(xiě)的hashcode方法。

public class User {
    int id;
    String name;

    public User(int i, String name) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return id == user.id &&
                name.equals(user.name);
    }


}

在這里插入圖片描述

通過(guò)運(yùn)行結(jié)果,我們可以看到,hashcode方法返回的hash值不一致,但是我們的equlas方法依舊返回的是true,因?yàn)檫@個(gè)equlas方法是我們重寫(xiě)過(guò)了,在調(diào)用的時(shí)候,就不在去掉Object的equlas方法,進(jìn)行比較內(nèi)存地址,而是按照我們的重寫(xiě)規(guī)則:如果兩個(gè)user對(duì)象的id和name相同,那么就是同一個(gè)對(duì)象,所以到這里,你是不是就具體的理解了hashcode方法個(gè)equlas之間的關(guān)系呢?

如果還是不明白,那再看一次,我們現(xiàn)在把User類的equlas方法和hashcode方法都寫(xiě)上,但是呢?我會(huì)創(chuàng)建兩i不同的User對(duì)象(也就是id和name都不一樣)。

public class User {
    int id;
    String name;

    public User(int i, String name) {
        this.id = id;
        this.name = name;
    }


    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return id == user.id &&
                name.equals(user.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

在這里插入圖片描述

由此可以看到,雖然hashcode方法返回的hash值相同,但是通過(guò)equlas方法進(jìn)行比較,返回的直接就是false。

這里我們說(shuō)的有點(diǎn)啰嗦了哈,本來(lái)三言兩語(yǔ)就可以說(shuō)清楚的,但是怕新手朋友不理解,所以就多啰嗦了幾句,這個(gè)文章是我個(gè)人的理解,如果有不正確的希望你多參考書(shū)以及官網(wǎng)進(jìn)行比對(duì),畢竟眼見(jiàn)為實(shí)嘛,我也不敢完全保證我就是對(duì)的。

到此這篇關(guān)于詳解Java中的hashcode的文章就介紹到這了,更多相關(guān)Java hashcode內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java編程題之從上往下打印出二叉樹(shù)

    java編程題之從上往下打印出二叉樹(shù)

    這篇文章主要為大家詳細(xì)介紹了java編程題之從上往下打印出二叉樹(shù),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • Java @PostMapping和@GetMapping方法使用詳解

    Java @PostMapping和@GetMapping方法使用詳解

    這篇文章主要介紹了Java @PostMapping和@GetMapping方法使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-03-03
  • java并發(fā)編程之深入理解Synchronized的使用

    java并發(fā)編程之深入理解Synchronized的使用

    文詳細(xì)講述了線程、進(jìn)程的關(guān)系及在操作系統(tǒng)中的表現(xiàn),這是多線程學(xué)習(xí)必須了解的基礎(chǔ)。本文將接著講一下Java線程同步中的一個(gè)重要的概念synchronized,希望能夠給你有所幫助
    2021-06-06
  • Java中的StringTokenizer實(shí)現(xiàn)字符串切割詳解

    Java中的StringTokenizer實(shí)現(xiàn)字符串切割詳解

    這篇文章主要介紹了Java中的StringTokenizer實(shí)現(xiàn)字符串切割詳解,java.util工具包提供了字符串切割的工具類StringTokenizer,Spring等常見(jiàn)框架的字符串工具類(如Spring的StringUtils),需要的朋友可以參考下
    2024-01-01
  • Mybatis配置返回為修改影響條數(shù)方式

    Mybatis配置返回為修改影響條數(shù)方式

    這篇文章主要介紹了Mybatis配置返回為修改影響條數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • java實(shí)現(xiàn)屏蔽詞功能

    java實(shí)現(xiàn)屏蔽詞功能

    這篇文章主要介紹了java實(shí)現(xiàn)屏蔽詞功能,類似貼吧里面屏蔽各種用戶的發(fā)帖內(nèi)容,感興趣的小伙伴們可以參考一下
    2015-12-12
  • java 遞歸查詢所有子節(jié)點(diǎn)id的方法實(shí)現(xiàn)

    java 遞歸查詢所有子節(jié)點(diǎn)id的方法實(shí)現(xiàn)

    在多層次的數(shù)據(jù)結(jié)構(gòu)中,經(jīng)常需要查詢一個(gè)節(jié)點(diǎn)下的所有子節(jié)點(diǎn),本文主要介紹了java 遞歸查詢所有子節(jié)點(diǎn)id的方法實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-03-03
  • Java +Tomcat + SpringMVC實(shí)現(xiàn)頁(yè)面訪問(wèn)示例解析

    Java +Tomcat + SpringMVC實(shí)現(xiàn)頁(yè)面訪問(wèn)示例解析

    這篇文章主要介紹了Java +Tomcat + SpringMVC實(shí)現(xiàn)頁(yè)面訪問(wèn)示例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 關(guān)于WeakhashMap與HashMap之間的區(qū)別和聯(lián)系

    關(guān)于WeakhashMap與HashMap之間的區(qū)別和聯(lián)系

    這篇文章主要介紹了關(guān)于WeakhashMap與HashMap之間的區(qū)別和聯(lián)系,WeakHashMap從名字可以得知主要和Map有關(guān),不過(guò)還有一個(gè)Weak,我們就更能自然而然的想到這里面還牽扯到一種弱引用結(jié)構(gòu),因此想要徹底搞懂,我們還需要知道四種引用,需要的朋友可以參考下
    2023-09-09
  • java數(shù)據(jù)結(jié)構(gòu)與算法數(shù)組模擬隊(duì)列示例詳解

    java數(shù)據(jù)結(jié)構(gòu)與算法數(shù)組模擬隊(duì)列示例詳解

    這篇文章主要為大家介紹了java數(shù)據(jù)結(jié)構(gòu)與算法數(shù)組模擬隊(duì)列示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06

最新評(píng)論