簡單的 HashMap 實現

N3verL4nd發表於2018-03-16
package test;

import java.util.*;

public class SimpleHashMap<K, V> extends AbstractMap<K, V> {
    // 連結串列陣列長度
    static final int SIZE = 997;

    /**
     * 建立一個連結串列陣列
     * 連結串列裡面存放的是 MapEntry<K, V>
     * 每個 buckets[i] 都對應一個LinkedList<MapEntry<K, V>>
     * 雖然不能建立泛型陣列,但是可以建立泛型陣列的引用
     */
    @SuppressWarnings("unchecked")
    private LinkedList<Node<K, V>>[] buckets = new LinkedList[SIZE];

    /**
     * 將鍵值對放入 HashMap
     *
     * @param key   鍵
     * @param value 值
     * @return
     */
    @Override
    public V put(K key, V value) {
        V oldValue = null;

        // 這裡就是通過對鍵雜湊然後取餘
        // 它代表了物件(LinkedList-->具有相同的索引)在陣列裡的位置,既陣列下標
        int index = Math.abs(key.hashCode()) % SIZE;

        if (buckets[index] == null) {
            // 如果是第一次雜湊到這個陣列下標,那麼就生成一個新的 LinkedList,連結串列裡面儲存的是 MapEntry<K,V>
            buckets[index] = new LinkedList<>();
        }

        LinkedList<Node<K, V>> bucket = buckets[index];
        // 根據鍵值對構建 hashMap 裡儲存的結點
        Node<K, V> node = new Node<>(key, value);

        boolean found = false;
        ListIterator<Node<K, V>> it = bucket.listIterator();
        while (it.hasNext()) {
            Node<K, V> iPair = it.next();
            if (iPair.getKey().equals(key)) {
                // 如果存在舊的鍵值對,就用新的 value 代替舊的 value; key 保持不變
                oldValue = iPair.getValue();
                it.set(node);
                found = true;
                break;
            }
        }
        if (!found) {
            // 如果是一個新的鍵值,那麼直接新增到這個 LinkedList 中
            buckets[index].add(node);
        }

        return oldValue;
    }

    /**
     * 根據鍵返回value
     *
     * @param key 鍵
     * @return 存在該 key 則返回相應的 value;否則返回 null
     */
    @Override
    public V get(Object key) {
        // 獲得相應的 LinkedList 對應的索引
        // 為什麼要用 LinkedList: 因為 hashcode 方法產生的雜湊碼不能完全確定一個物件(發生碰撞),即不同的物件雜湊到同一個陣列下標
        // 解決辦法: 定義一個 List 把發生碰撞的物件放入其中,然後執行線性查詢
        // hashcode()相等的兩個物件,equals()不一定相等;而equals()相等的兩個物件,hashcode()一定相等
        int index = Math.abs(key.hashCode()) % SIZE;
        if (buckets[index] == null) {
            return null;
        }
        for (Node<K, V> node : buckets[index]) {
            if (node.getKey().equals(key)) {
                return node.getValue();
            }
        }
        return null;
    }

    /**
     * @return 返回鍵值對所對應的 Set
     */
    @Override
    public Set<Entry<K, V>> entrySet() {
        Set<Map.Entry<K, V>> set = new HashSet<>();
        for (LinkedList<Node<K, V>> bucket : buckets) {
            if (bucket == null) continue;
            set.addAll(bucket);
        }
        return set;
    }

    /**
     * 內部儲存的結點
     *
     * @param <K> 鍵
     * @param <V> 值
     */
    static class Node<K, V> implements Map.Entry<K, V> {
        private K key;
        private V value;

        Node(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 newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        @Override
        public boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
                return Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue());
            }
            return false;
        }

        @Override
        public String toString() {
            return key + "=" + value;
        }
    }

    public static void main(String[] args) {
        SimpleHashMap<String, String> m = new SimpleHashMap<>();
        m.put("Aa", "AAAAA");
        m.put("BB", "BBBBB");
        m.put("CC", "CCCCC");
        System.out.println(m);
        System.out.println(m.get("Aa"));
        System.out.println(m.entrySet());
    }
}
複製程式碼

hashCode() 的作用:提高查詢效率(使用O(1)的時間來縮小查詢範圍) 故基於雜湊的Map和Set儲存的物件都要是實現 hashCode()。 一般的hashCode()equals() 需要同時實現。 並且要滿足

hashcode()相等的兩個物件,equals()不一定相等;而equals()相等的兩個物件,hashcode()一定相等

雖然不是強制的規範,但是一般的實現都要滿足這個條件!


似乎有種似曾相識的感覺? 沒錯 switch 對 String 的處理就是先通過比較hashCode()返回值,然後再用equals()比較。

public class TestString {
    public static void main(String[] args) {
        String str = "world";
        switch (str) {
            case "hello":
                System.out.println("hello");
                break;
            case "world":
                System.out.println("world");
                break;
            default: break;
        }
    }
}
複製程式碼

反編譯後:

public static void main(String args[]) {
       String str = "world";
       String s;
       switch((s = str).hashCode()) {
          case 99162322:
               if(s.equals("hello"))
                   System.out.println("hello");
               break;
          case 113318802:
               if(s.equals("world"))
                   System.out.println("world");
               break;
          default: break;
       }
  }
複製程式碼

相關文章