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次冪時):

將數據整理完成后,做了一個折線圖:


根據實驗結果,我得到結論和我的想法一致,兩者碰撞率應該會趨於一致,這個擾動函數好像是沒用的,更確切的說這一步所有的擾動函數應該都是沒用的。。。。。
我做完實驗后,不知道是喜是悲,也不知是我哪里想偏了,想錯了,還是因為概率論什么的沒學,或者是實驗的時候哪里寫錯了,希望各位能糾正一下我錯誤的想法


免責聲明!

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



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