步驟 1 : List查找的低效率html
假設在List中存放着無重複名稱,沒有順序的2000000個Hero
要把名字叫作「hero 1000000」的對象找出來
List的作法是對每個進行挨個遍歷,直到找到名字叫作「hero 1000000」的英雄。
最差的狀況下,須要遍歷和比較2000000次,才能找到對應的英雄。
測試邏輯:java
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 作一樣的查找數組
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進行查詢,很是快原理。
這是一種用空間換時間的思惟方式
步驟 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; } }