hashcode返回值可能為負數


hashcode返回值可能為負數

公司內部做服務優化,線上單機部署多個redis實例,路由到同一台機器上的用戶,id號段假設為1000000~9999999,同一個的用戶信息肯定是要固定到某個redis實例的,所以需要一個算法,保證每次選擇的redis實例都是一樣的。最容易想到的就是用id對redis實例個數取余,但如果以后id換為字符串呢?這種取余算法就不合適了。

之后想到可以使用hashcode,把id轉換為String,對之取hashcode,然后用hashcode對redis實例個數取余,同樣的字符串,計算得到的hashcode肯定是一致的,也就解決了一致性選擇redis實例的問題。部分代碼如下:

//key為String類型



int hash = 0;



if (StringUtils.isNotBlank(key)) {



    hash = key.hashCode()



}



//根據basePort做偏移,選取不同的redis實例



jedis = getClientFromPool(host, config.basePort + hashcode % redisServerCount);

考慮到字符串可能為空(程序bug或未知原因),默認hashcode為0,根據不同的hashcode,對basePort做偏移初始化的jedis。本以為代碼寫的很健壯,運行起來肯定萬事大吉,直接手工。

但是,事與願違,服務運行時總是報奇怪的錯誤如下,究其原因就是,jedis沒有初始化,無法從jedis的連接池中獲取鏈接,本機redis服務監聽的端口好好的,redis服務本身沒有問題,怎么會這樣呢?

java.lang.NullPointerException



        at com.xiaomi.xmpush.redis.JedisClient.set(JedisClient.java:490)



        at com.xiaomi.xmpush.redis.JedisClient.set(JedisClient.java:484)



 



 



redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool



        at redis.clients.util.Pool.getResource(Pool.java:53)



        at redis.clients.jedis.JedisPool.getResource(JedisPool.java:226)



        at com.xiaomi.xmpush.redis.JedisClient.getClientFromPool(JedisClient.java:69)



 



 



 

那就只可能是代碼的問題,無法創建redis實例的鏈接,那可能是port選的不對?port為什么會不對呢,hashcode取余,保證不會溢出啊!心中很納悶,懷疑可能是hashcode搞得鬼,果然,增加了一些debug log之后,竟然發現hashcode的值可能為負數。比如這貨

long userId = 56800004874L;



System.out.println(String.valueOf(userId).hashCode() );



 



輸出:-2082984168

再看hashcode的源碼,返回值是int型,確實可能是負數。。

    /**



     * Returns a hash code for this string. The hash code for a



     * {@code String} object is computed as



     * <blockquote><pre>



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



     * </pre></blockquote>



     * using {@code int} arithmetic, where {@code s[i]} is the



     * <i>i</i>th character of the string, {@code n} is the length of



     * the string, and {@code ^} indicates exponentiation.



     * (The hash value of the empty string is zero.)



     *



     * @return  a hash code value for this object.



     */



    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;



    }

大家都知道,int型的值取值范圍為Integer.MIN_VALUE(-2147483648)~Integer.MAX_VALUE(2147483647),那怎么修改呢?可以想到取絕對值,那Integer.MIN_VALUE的絕對值是多少呢?正數發生了溢出啊?

Math.abs(Integer.MIN_VALUE) = Integer.MIN_VALUE  

看來這樣行不通,如果對hash做數據偏移,統一加2147483647呢?No no,這樣更是錯誤,因為更會發生數據溢出,會產生負數,有一種牛逼的辦法就是使用位與操作:

hash = key.hashCode() & Integer.MAX_VALUE; // caution, make value not minus

對於計算得到的hash值,強制把符號位去掉,保證結果只在正數區間,至此,把hashcode關進籠子了,問題解決。






文章轉載自:https://blog.csdn.net/chanllenge/article/details/85673259


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM