關於HashMap中的擾動函數的疑問


         最近再看jdk8的hashmap源碼,當看到這一步的時候有點疑問,去網上搜了一下,看到的全部文章基本上都是一篇抄一篇的(反正目前各大社區就是這麼個情況),那個意思就是讓高16位也參與運算,增長結果的隨機性,減少hash碰撞???

       乍一聽好像是那麼回事,可是越想越不對勁;我怎麼都以爲是不管怎麼運算最後不都是看低幾位嗎,在哪一個固定的長度裏每一個數出現的機率不仍是隨機的嗎(1/length);高位參與運算以後確定能保證的是原來低位相同的值更加不一樣,可是不能保證原本不一樣的值運算後仍是不通吧。懷着各類疑問,簡單作了下實驗。
與運算基數從2的4次冪到16次冪,分別對原始hash和擾動函數後的值進行統計
實驗數據爲隨機8爲的字符串值,平時咱們用字符串做爲key的狀況最多吧
最後統計結果爲:100次碰撞率的均值
代碼:

import com.alibaba.fastjson.JSON;

        import java.util.ArrayList;
        import java.util.Random;
        import java.util.List;

/**
 * @description:
 * @author: wukong
 * @remark: create wukong 2019/12/26 22:49
 */
public class HashTest {
    public static void main(String[] args) {
        int length = 1 << 8;
        List<Double> doubles = new ArrayList<>(100);
        List<Double> double2s = new ArrayList<>(100);
        // 測試次數
        int count = 100;
        for (int i = 0; i < count; i++) {
            hashCalculate(length, doubles, double2s);
        }
        System.out.println("均值1:" + doubles.stream().mapToDouble((item) -> item).summaryStatistics().getAverage());
        System.out.println("均值2:" + double2s.stream().mapToDouble((item) -> item).summaryStatistics().getAverage());
        System.out.println("集合1:" + JSON.toJSON(doubles));
        System.out.println("集合2" + JSON.toJSON(double2s));
    }

    /**
     * @Description: hash碰撞率計算
     */
    private static void hashCalculate(int length, List<Double> doubles, List<Double> double2s) {
        int cardinal = length - 1;
        int load = (int) (length * 0.75);
        int crash = 0;
        int crash2 = 0;
        List<Integer> list = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        for (int i = 0; i < load; i++) {
            // 隨機key獲取哈希值
            int hash = getRandomString().hashCode();
            // 直接與基數進行與運算
            int result = cardinal & hash;
            // jdk8中hashmap擾動函數
            int disturbHash = hash ^ (hash >>> 16);
            // 擾動後的值與運算
            int result2 = cardinal & disturbHash;
            //統計直接運算碰撞次數
            if (!list.contains(result)) {
                list.add(result);
            } else {
                crash++;
            }
            //統計擾亂後碰撞次數
            if (!list2.contains(result2)) {
                list2.add(result2);
            } else {
                crash2++;
            }
        }
        double crashProbability = crash / (double) length;
        double crashProbability2 = crash2 / (double) length;
        doubles.add(crashProbability);
        double2s.add(crashProbability2);
//      System.out.println("當長度爲" + length + "時,hash值直接與運算的碰撞率爲:" + crashProbability);
//      System.out.println("當長度爲" + length + "時,擾動函數以後與運算的碰撞率爲:" + crashProbability2);
    }

    /**
     * @Description: 獲取隨機key字符串
     */
    private static String getRandomString() {
        String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        int length = 8;
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(62);
            sb.append(str.charAt(number));
        }
        return sb.toString();
    }
}

運行結果示例(如下長度爲2的16次冪時):

將數據整理完成後,作了一個折線圖:

根據實驗結果,我獲得結論和個人想法一致,二者碰撞率應該會趨於一致,這個擾動函數好像是沒用的,更確切的說這一步全部的擾動函數應該都是沒用的。。。。。 我作完實驗後,不知道是喜是悲,人家設計的怎麼會有問題,還有更多的人也認同那樣的設計,可是我卻越想越以爲這一步是不可能下降碰撞率的。。。。也不知是我哪裏想偏了,想錯了,仍是由於機率論什麼的沒學,或者是實驗的時候哪裏寫錯了,但願各位能糾正一下我錯誤的想法

相關文章
相關標籤/搜索