一、前情回顧:在程序中有時候需要存放對象,容器應運而生。容器分為集合和Map。集合在這里不說,說說Map。Map在英語中是地圖的意思,這個名字真是起的好,可以讓人顧名思義。Map,就是存放鍵值對的結構。也就是說,只要找到鍵,就能找到對應的值,就跟查字典一樣。
二、Map工作效率的深層原理:
1.上面說到查詢map就是查詢鍵,只要鍵找得到,值就會對應的找得到。所以怎么找到鍵,就是訪問Map的效率的瓶頸所在。
2.那么如何找到鍵呢?其中一個好辦法就是把鍵排序,然后按照二分法查找。二分法就不用介紹了吧?
3.散列(HashMap)則更進一步。他把鍵相關的信息保存在一個數組中。因為數組時訪問速度最快的數據結構。這樣可以大大提高效率。但是數組中存儲的是鍵的相關信息,而不是鍵本身。因為Map的大小是不確定的,而數組的大小是確定的,所以問題來了,怎么把不確定size的Map的鍵的相關信息存儲在數組中呢?
4.那就是數組並不保存鍵本身,只保存於鍵相關的信息,這個相關信息就是每一個鍵對象生成的數字,將其作為數組的下表,這個數字就是散列碼,即hashCode()方法。
5.不同的鍵可以產生相同的散列碼,對應於數組的相同下標。這樣就解決了數組大小不變的問題。即:數組中的每一個具體的位置,可以根據Map的大小存儲一個或多個Map基本元素,這樣就會保證Map可以自由調節大小。Map小了,把數組填不滿,Map大了,就往數組的同一個位置多填幾個值。其數據結構如下圖:
代碼如下
package test; import java.util.Map; /** * @author 笑傲獨行俠 * @description: * @Date: 2019/7/9 13:55 */ public class MapEntry<K, V> implements Map.Entry<K, V> { private K key; private V value; public MapEntry(K key, V value) { this.key = key; this.value = value; } @Override public K getKey() { return key; } @Override public V getValue() { return value; } @Override public V setValue(V value) { V oldValue = this.value; this.value = value; return oldValue; } }
package test; import java.util.*; /** * @author 笑傲獨行俠 * @description: 理解HashMap的深層原理 * @Date: 2019/7/9 11:33 */ public class SimpleHashMap<K, V> extends AbstractMap<K, V> { static final int SIZE = 997;//定義數組的大小,盡量大一下,使得一個map盡量存入不同的下標數組中 LinkedList<MapEntry<K, V>>[] buckets = new LinkedList[SIZE];//定義一個數組,存放鍵的相關信息,數組中是List public V put(K key, V value) { V oldValue = null; //先計算key的hashCode值,再處理一下算出對應的數組下標 int index = Math.abs(key.hashCode()) % SIZE; //如果該下標對應的還沒有值,就先new一個List if (buckets[index] == null) { buckets[index] = new LinkedList<MapEntry<K, V>>(); } //如果該下標已經有一個key存放在其中,就將當前key添加到該List中。 LinkedList<MapEntry<K, V>> bucket = buckets[index]; MapEntry<K, V> pair = new MapEntry<K, V>(key, value); boolean found = false; ListIterator<MapEntry<K, V>> it = bucket.listIterator(); while (it.hasNext()) { MapEntry<K, V> iPair = it.next(); if (iPair.getKey().equals(key)) { oldValue = iPair.getValue(); it.set(pair); found = true; break; } } //定義一個Boolean變量,如果要存入的鍵已經存在,則替換該鍵對應的值,如果不存在則添加 if (!found) buckets[index].add(pair); return oldValue; } @Override public V get(Object key) { int index = Math.abs(key.hashCode()) % SIZE; if (buckets[index] == null) return null; for (MapEntry<K, V> iPair : buckets[index]) { if (iPair.getKey().equals(key)) return iPair.getValue(); } return null; } @Override public Set<Entry<K, V>> entrySet() { Set<Map.Entry<K, V>> set = new HashSet<>(); for (LinkedList<MapEntry<K, V>> bucket : buckets) { if (bucket == null) { continue; } for (MapEntry<K, V> mPair : bucket) { set.add(mPair); } } return set; } public static void main(String[] args) { SimpleHashMap<String, String> m = new SimpleHashMap<>(); m.put("111", "huhhu"); m.put("111", "huhhu"); m.put("111", "huhhu"); m.put("111", "huhhu"); System.out.println(m); System.out.println(m.get("ERITREA")); System.out.println(m.entrySet()); } }