HashMap繼承自抽象類AbstractMap,抽象類AbstractMap實現了Map接口。關係圖如下所示:
- import java.util.*;
- public class SimpleMap<K,V> extends AbstractMap<K,V> {
- //keys存儲所有的鍵
- private List<K> keys = new ArrayList<K>();
- //values存儲所有的值
- private List<V> values = new ArrayList<V>();
- /**
- * 該方法獲取Map中所有的鍵值對
- */
- @Override
- public Set entrySet() {
- Set<Map.Entry<K, V>> set = new SimpleSet<Map.Entry<K,V>>();
- //keys的size和values的size應該一直是一樣大的
- Iterator<K> keyIterator = keys.iterator();
- Iterator<V> valueIterator = values.iterator();
- while(keyIterator.hasNext() && valueIterator.hasNext()){
- K key = keyIterator.next();
- V value = valueIterator.next();
- SimpleEntry<K,V> entry = new SimpleEntry<K,V>(key, value);
- set.add(entry);
- }
- return set;
- }
- @Override
- public V put(K key, V value) {
- V oldValue = null;
- int index = this.keys.indexOf(key);
- if(index >= 0){
- //keys中已經存在鍵key,更新key對應的value
- oldValue = this.values.get(index);
- this.values.set(index, value);
- }else{
- //keys中不存在鍵key,將key和value作爲鍵值對添加進去
- this.keys.add(key);
- this.values.add(value);
- }
- return oldValue;
- }
- @Override
- public V get(Object key) {
- V value = null;
- int index = this.keys.indexOf(key);
- if(index >= 0){
- value = this.values.get(index);
- }
- return value;
- }
- @Override
- public V remove(Object key) {
- V oldValue = null;
- int index = this.keys.indexOf(key);
- if(index >= 0){
- oldValue = this.values.get(index);
- this.keys.remove(index);
- this.values.remove(index);
- }
- return oldValue;
- }
- @Override
- public void clear() {
- this.keys.clear();
- this.values.clear();
- }
- @Override
- public Set keySet() {
- Set<K> set = new SimpleSet<K>();
- Iterator<K> keyIterator = this.keys.iterator();
- while(keyIterator.hasNext()){
- set.add(keyIterator.next());
- }
- return set;
- }
- @Override
- public int size() {
- return this.keys.size();
- }
- @Override
- public boolean containsValue(Object value) {
- return this.values.contains(value);
- }
- @Override
- public boolean containsKey(Object key) {
- return this.keys.contains(key);
- }
- @Override
- public Collection values() {
- return this.values();
- }
- }
- public Collection<V> values() {
- if (values == null) {
- values = new AbstractCollection<V>() {
- public Iterator<V> iterator() {
- return new Iterator<V>() {
- private Iterator<Entry<K,V>> i = entrySet().iterator();
- public boolean hasNext() {
- return i.hasNext();
- }
- public V next() {
- return i.next().getValue();
- }
- public void remove() {
- i.remove();
- }
- };
- }
- public int size() {
- return AbstractMap.this.size();
- }
- public boolean isEmpty() {
- return AbstractMap.this.isEmpty();
- }
- public void clear() {
- AbstractMap.this.clear();
- }
- public boolean contains(Object v) {
- return AbstractMap.this.containsValue(v);
- }
- };
- }
- return values;
- }
大家可以看到,代碼不少,基本的思路是先通過entrySet生成包含所有鍵值對的Set,然後通過迭代獲取其中的value值。其中生成包含所有鍵值對的Set肯定需要開銷,所以我們在自己的實現裏面重寫了values方法,就一句話,return this.values,直接返回我們的values字段。所以我們重寫大部分方法的目的都是讓方法的實現更快更簡潔。
以下是我們自己實現的鍵值對類SimpleEntry,實現了Map.Entry<K,V>接口,代碼如下:
- import java.util.Map;
- //Map中存儲的鍵值對,鍵值對需要實現Map.Entry這個接口
- public class SimpleEntry<K,V> implements Map.Entry<K, V>{
- private K key = null;//鍵
- private V value = null;//值
- public SimpleEntry(K k, V v){
- this.key = k;
- this.value = v;
- }
- @Override
- public K getKey() {
- return this.key;
- }
- @Override
- public V getValue() {
- return this.value;
- }
- @Override
- public V setValue(V v) {
- V oldValue = this.value;
- this.value = v;
- return oldValue;
- }
- }
以下是我們自己實現的集合類SimpleSet,繼承自抽象類AbstractSet<K,V>,代碼如下:
- import java.util.AbstractSet;
- import java.util.ArrayList;
- import java.util.Iterator;
- public class SimpleSet<E> extends AbstractSet<E> {
- private ArrayList<E> list = new ArrayList<E>();
- @Override
- public Iterator<E> iterator() {
- return this.list.iterator();
- }
- @Override
- public int size() {
- return this.list.size();
- }
- @Override
- public boolean contains(Object o) {
- return this.list.contains(o);
- }
- @Override
- public boolean add(E e) {
- boolean isChanged = false;
- if(!this.list.contains(e)){
- this.list.add(e);
- isChanged = true;
- }
- return isChanged;
- }
- @Override
- public boolean remove(Object o) {
- return this.list.remove(o);
- }
- @Override
- public void clear() {
- this.list.clear();
- }
- }
我們測試下我們寫的SimpleMap這個類,測試包括兩部分,一部分是測試我們寫的SimpleMap是不是正確,第二部分測試性能如何,測試代碼如下:
- import java.util.HashMap;
- import java.util.HashSet;
- import java.util.Map;
- public class Test {
- public static void main(String[] args) {
- //測試SimpleMap的正確性
- SimpleMap<String, String> map = new SimpleMap<String, String>();
- map.put("iSpring", "27");
- System.out.println(map);
- System.out.println(map.get("iSpring"));
- System.out.println("-----------------------------");
- map.put("iSpring", "28");
- System.out.println(map);
- System.out.println(map.get("iSpring"));
- System.out.println("-----------------------------");
- map.remove("iSpring");
- System.out.println(map);
- System.out.println(map.get("iSpring"));
- System.out.println("-----------------------------");
- //測試性能如何
- testPerformance(map);
- }
- public static void testPerformance(Map<String, String> map){
- map.clear();
- for(int i = 0; i < 10000; i++){
- String key = "key" + i;
- String value = "value" + i;
- map.put(key, value);
- }
- long startTime = System.currentTimeMillis();
- for(int i = 0; i < 10000; i++){
- String key = "key" + i;
- map.get(key);
- }
- long endTime = System.currentTimeMillis();
- long time = endTime - startTime;
- System.out.println("遍歷時間:" + time + "毫秒");
- }
- }
- //創建HashMap的實例
- HashMap<String, String> map = new HashMap<String, String>();
- //測試性能如何
- testPerformance(map);
- @Override
- public V put(K key, V value) {
- V oldValue = null;
- int index = this.keys.indexOf(key);
- if(index >= 0){
- //keys中已經存在鍵key,更新key對應的value
- oldValue = this.values.get(index);
- this.values.set(index, value);
- }else{
- //keys中不存在鍵key,將key和value作爲鍵值對添加進去
- this.keys.add(key);
- this.values.add(value);
- }
- return oldValue;
- }
需要性能開銷的主要是this.keys.indexOf(key)這句代碼,這句代碼從ArrayList中查找指定元素的索引,本質就是從數組開頭走,往後找,直至數組的末尾。如下圖所示:
- public V put(K key, V value) {
- if (key == null)
- return putForNullKey(value);
- int hash = hash(key.hashCode());
- int i = indexFor(hash, table.length);
- for (Entry<K,V> e = table[i]; e != null; e = e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
- modCount++;
- addEntry(hash, key, value, i);
- return null;
- }
在put方法中,,調用了對象的hashCode方法,該方法返回一個int類型的值,是個初始的哈希值,這個值就相當於車牌號,例如"魯E.DE829",HashMap中有個hash方法,該hash方法將我們得到的初始的哈希值做進一步處理,得到最終的哈希值,就好比我們將車牌號傳入hash方法,然後返回該存放車輛的大桶,即返回"魯",這樣HashMap就把這輛車放到標有“魯”的大桶裏面了。上面說到的hash方法叫做哈希函數,專門負責根據傳入的值返回指定的最終哈希值,具體實現如下:
- static int hash(int h) {
- // This function ensures that hashCodes that differ only by
- // constant multiples at each bit position have a bounded
- // number of collisions (approximately 8 at default load factor).
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
- }
- void addEntry(int hash, K key, V value, int bucketIndex) {
- Entry<K,V> e = table[bucketIndex];
- table[bucketIndex] = new Entry<>(hash, key, value, e);
- if (size++ >= threshold)
- resize(2 * table.length);
- }
- public V get(Object key) {
- if (key == null)
- return getForNullKey();
- int hash = hash(key.hashCode());
- for (Entry<K,V> e = table[indexFor(hash, table.length)];
- e != null;
- e = e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
- return e.value;
- }
- return null;
- }
在get方法中,也是先調用了對象的hashCode方法,就相當於車牌號,然後再將該值讓hash函數處理得到最終的哈希值,也就是桶的索引。然後我們再去這個標有“魯”的桶裏面去找我們的鍵值對,首先先取出桶裏面第一個鍵值對,比對一下是不是我們要找的元素,如果是就直接返回了,如果不是就通過鍵值對的next順藤摸瓜通過單向鏈表繼續找下去,直至找到。 如下圖所示:
- import java.util.HashMap;
- public class Car {
- private final String num;//車牌號
- public Car(String n){
- this.num = n;
- }
- public String getNum(){
- return this.num;
- }
- @Override
- public boolean equals(Object obj) {
- if(obj == null){
- return false;
- }
- if(obj instanceof Car){
- Car car = (Car)obj;
- return this.num.equals(car.num);
- }
- return false;
- }
- public static void main(String[] args){
- HashMap<Car, String> map = new HashMap<Car, String>();
- String num = "魯E.DE829";
- Car car1 = new Car(num);
- Car car2 = new Car(num);
- System.out.println("Car1 hash code: " + car1.hashCode());
- System.out.println("Car2 hash code: " + car2.hashCode());
- System.out.println("Car1 equals Car2: " + car1.equals(car2));
- map.put(car1, new String("Car1"));
- map.put(car2, new String("Car2"));
- System.out.println("map.size(): " + map.size());
- }
- }
Car2 hash code: 2027651571
Car1 equals Car2: true
map.size(): 2
- @Override
- public int hashCode() {
- return this.num.hashCode();
- }
Car2 hash code: 607836628
Car1 equals Car2: true
map.size(): 1