最近再看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次冪時):

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

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