Java自學-集合框架 hashCode原理

Java hashCode原理

步驟 1 : List查找的低效率html

假設在List中存放着無重複名稱,沒有順序的2000000個Hero
要把名字叫作「hero 1000000」的對象找出來
List的作法是對每個進行挨個遍歷,直到找到名字叫作「hero 1000000」的英雄。
最差的狀況下,須要遍歷和比較2000000次,才能找到對應的英雄。
測試邏輯:java

  1. 初始化2000000個對象到ArrayList中
  2. 打亂容器中的數據順序
  3. 進行10次查詢,統計每一次消耗的時間
    不一樣計算機的配置狀況下,所花的時間是有區別的。 在本機上,花掉的時間大概是600毫秒左右

List查找的低效率

package collection;
     
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
     
import charactor.Hero;
     
public class TestCollection {
    public static void main(String[] args) {
        List<Hero> heros = new ArrayList<Hero>();
            
        for (int j = 0; j < 2000000; j++) {
            Hero h = new Hero("Hero " + j);
            heros.add(h);
        }
            
        // 進行10次查找,觀察大致的平均值
        for (int i = 0; i < 10; i++) {
            // 打亂heros中元素的順序
            Collections.shuffle(heros);
             
            long start = System.currentTimeMillis();
     
            String target = "Hero 1000000";
     
            for (Hero hero : heros) {
                if (hero.name.equals(target)) {
                    System.out.println("找到了 hero!" );
                    break;
                }
            }
            long end = System.currentTimeMillis();
            long elapsed = end - start;
            System.out.println("一共花了:" + elapsed + " 毫秒");
        }
             
    }
}

步驟 2 : HashMap的性能表現算法

使用HashMap 作一樣的查找數組

  1. 初始化2000000個對象到HashMap中。
  2. 進行10次查詢
  3. 統計每一次的查詢消耗的時間
    能夠觀察到,幾乎不花時間,花費的時間在1毫秒之內

HashMap的性能表現

package collection;
  
import java.util.HashMap;
  
import charactor.Hero;
  
public class TestCollection {
    public static void main(String[] args) {
          
        HashMap<String,Hero> heroMap = new HashMap<String,Hero>();
        for (int j = 0; j < 2000000; j++) {
            Hero h = new Hero("Hero " + j);
            heroMap.put(h.name, h);
        }
        System.out.println("數據準備完成");
  
        for (int i = 0; i < 10; i++) {
            long start = System.currentTimeMillis();
              
            //查找名字是Hero 1000000的對象
            Hero target = heroMap.get("Hero 1000000");
            System.out.println("找到了 hero!" + target.name);
              
            long end = System.currentTimeMillis();
            long elapsed = end - start;
            System.out.println("一共花了:" + elapsed + " 毫秒");
        }
  
    }
}

步驟 3 : HashMap原理與字典dom

在展開HashMap原理的講解以前,首先回憶一下你們初中和高中使用的漢英字典。性能

好比要找一個單詞對應的中文意思,假設單詞是Lengendary,首先在目錄找到Lengendary在第 555頁。學習

而後,翻到第555頁,這頁不僅一個單詞,可是量已經不多了,逐一比較,很快就定位目標單詞Lengendary。測試

555至關於就是Lengendary對應的hashcodecode

步驟 4 : 分析HashMap性能卓越的緣由htm

-----hashcode概念-----
全部的對象,都有一個對應的hashcode(散列值)
好比字符串「gareen」對應的是1001 (實際上不是,這裏是方便理解,假設的值)
好比字符串「temoo」對應的是1004
好比字符串「db」對應的是1008
好比字符串「annie」對應的也是1008

-----保存數據-----
準備一個數組,其長度是2000,而且設定特殊的hashcode算法,使得全部字符串對應的hashcode,都會落在0-1999之間
要存放名字是"gareen"的英雄,就把該英雄和名稱組成一個鍵值對,存放在數組的1001這個位置上
要存放名字是"temoo"的英雄,就把該英雄存放在數組的1004這個位置上
要存放名字是"db"的英雄,就把該英雄存放在數組的1008這個位置上
要存放名字是"annie"的英雄,然而 "annie"的hashcode 1008對應的位置已經有db英雄了,那麼就在這裏建立一個鏈表,接在db英雄後面存放annie

-----查找數據-----
好比要查找gareen,首先計算"gareen"的hashcode是1001,根據1001這個下標,到數組中進行定位,(根據數組下標進行定位,是很是快速的) 發現1001這個位置就只有一個英雄,那麼該英雄就是gareen.
好比要查找annie,首先計算"annie"的hashcode是1008,根據1008這個下標,到數組中進行定位,發現1008這個位置有兩個英雄,那麼就對兩個英雄的名字進行逐一比較(equals),由於此時須要比較的量就已經少不少了,很快也就能夠找出目標英雄
這就是使用hashmap進行查詢,很是快原理。

這是一種用空間換時間的思惟方式

分析HashMap性能卓越的緣由
步驟 5 : HashSet判斷是否重複

HashSet的數據是不能重複的,相同數據不能保存在一塊兒,到底如何判斷是不是重複的呢?
根據HashSet和HashMap的關係,咱們瞭解到由於HashSet沒有自身的實現,而是裏面封裝了一個HashMap,因此本質上就是判斷HashMap的key是否重複。

再經過上一步的學習,key是否重複,是由兩個步驟判斷的:
hashcode是否同樣
若是hashcode不同,就是在不一樣的坑裏,必定是不重複的
若是hashcode同樣,就是在同一個坑裏,還須要進行equals比較
若是equals同樣,則是重複數據
若是equals不同,則是不一樣數據。

練習自定義字符串的hashcode

以下是Java API提供的String的hashcode生成辦法;

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

s[0] 表示第一位字符
n表示字符串的長度
本練習並非要求去理解這個算法,而是自定義一個簡單的hashcode算法,計算任意字符串的hashcode
由於String類不能被重寫,因此咱們經過一個靜態方法來返回一個String的hashcode

public static int hashcode(String)

若是字符串長度是0,則返回0。
不然: 獲取每一位字符,轉換成數字後,相加,最後乘以23

(s[0]+ s[1] + s[2] + s[3]+ s[n-1])*23.

若是值超過了1999,則取2000的餘數,保證落在0-1999之間。
若是是負數,則取絕對值。

隨機生成長度是2-10的不等的100個字符串,打印用本hashcode獲取的值分別是多少

答案
在這裏插入圖片描述

package collection;
 
public class TestCollection {
     
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            int length = (int) (Math.random()*8+2);
            String str = randomString(length);
            int hashcode = hashcode(str);
            System.out.printf("%-11s的自定義hashcode是:%d%n",str,hashcode);         
        }
         
    }
 
    private static int hashcode(String str) {
        // TODO Auto-generated method stub
        if(0==str.length())
            return 0;
         
        int hashcode = 0;
        char[]cs= str.toCharArray();
        for (int i = 0; i < cs.length; i++) {
            hashcode +=cs[i];
        }
        hashcode*=23;
        //取絕對值
        hashcode = hashcode<0?0-hashcode:hashcode;
        //落在0-1999之間
        hashcode %=2000;
         
        return hashcode;
    }
     
    private static String randomString(int length) {
        String pool = "";
        for (short i = '0'; i <= '9'; i++) {
            pool += (char) i;
        }
        for (short i = 'a'; i <= 'z'; i++) {
            pool += (char) i;
        }
        for (short i = 'A'; i <= 'Z'; i++) {
            pool += (char) i;
        }
        char cs[] = new char[length];
        for (int i = 0; i < cs.length; i++) {
            int index = (int) (Math.random() * pool.length());
            cs[i] = pool.charAt(index);
        }
        String result = new String(cs);
        return result;
    }
     
}
相關文章
相關標籤/搜索