手寫Hashmap第一版

     關於hashmap的問題,經常被問到。雖然看了一下源碼和相關博客,記憶還是不太深刻。發現只有手寫之後,對其中的運行原理纔有更深刻的理解。這裏記錄第一版,實現了存儲、查詢、刪除、擴容操作,是線程不安全的,且沒有實現迭代器功能。鏈表裏面沒有用到紅黑樹。一步步迭代,先記錄自己的第一步。

package www.dustin.li.collections;

/**
 * 手寫集合
 *
 * @param <K>
 * @param <V>
 */
public class MyHashMap<K, V> {

    private  int CAPACITY = 16;

    private  int SIZE = 0;

    private  int USED_SIZE = 0;

    private  float threshold = 0.75F;

    Node<K, V>[] nodes = new Node[CAPACITY];

    /**
     * 存儲鍵值對
     *
     * @param key
     * @param value
     * @return
     */
    public boolean put(K key, V value) {
        int index = this.getIndex(key);
        Node<K, V> node = null;
        try {
            node = nodes[index];
        } catch (Exception e) {
            System.out.println("hashCode=" + key.hashCode());
            System.out.println("index=" + index);
        }
        if (node == null) {
            System.out.println("插入,index=" + index + ",數組首位");
            node = new Node<>();
            node.setKey(key);
            node.setValue(value);
            nodes[index] = node;
            SIZE++;
            USED_SIZE++;
            if (checkThreshold()) {
                this.resize();
            }
        } else {
            while (node.nextNode != null) {
                if (node.getValue().equals(value)) {
                    System.out.println("插入,index=" + index + ",相同的鍵值對,直接返回");
                    return true;
                }
                node = node.nextNode;
            }
            Node<K, V> nextNode = new Node<>();
            nextNode.setKey(key);
            nextNode.setValue(value);
            node.nextNode = nextNode;
            SIZE++;
        }
        return true;
    }

    /**
     * 根據key獲取元素
     *
     * @param key
     * @return
     */
    public V get(K key) {
        int index = this.getIndex(key);
        Node<K, V> headNode = nodes[index];
        if (headNode == null) {
            return null;
        }
        if (headNode.getKey().equals(key)) {
            return headNode.getValue();
        }
        for (; ; ) {
            Node<K, V> nextNode = headNode.nextNode;
            if (nextNode == null) {
                System.out.println("根據key沒有找到對應的value");
                return null;
            }
            if (nextNode.getKey().equals(key)) {
                System.out.println("根據key找到對應的value");
                return nextNode.getValue();
            }
            headNode = nextNode;
            if (headNode == null) {
                return null;
            }
        }
    }

    /**
     * 移除元素
     *
     * @param key
     * @return
     */
    public V remove(K key) {
        int index = this.getIndex(key);
        Node<K, V> headNode = nodes[index];
        if (headNode == null) {
            System.out.println("根據key刪除鍵值對失敗,沒有找到對應數據,index=" + index + ",key=" + key);
            return null;
        }
        if (headNode.getKey().equals(key)) {
            SIZE--;
            USED_SIZE--;
            nodes[index] = headNode.nextNode;
            System.out.println("刪除鍵值對成功,直接刪除數組上的元素,index=" + index + ",key=" + key);
            return headNode.getValue();
        }
        for (; ; ) {
            Node<K, V> nextNode = headNode.nextNode;
            if (nextNode == null) {
                System.out.println("沒有找到對應的數據,index=" + index + ",key=" + key);
                return null;
            }
            if (key.equals(nextNode.getKey())) {
                SIZE--;
                headNode.nextNode = nextNode.nextNode;
                System.out.println("刪除鍵值對成功,刪除鏈表上的元素,index=" + index + ",key=" + key);
                return nextNode.getValue();
            }
            headNode = headNode.nextNode;
        }
    }

    /**
     * 檢查是否超過閥值
     *
     * @return
     */
    private boolean checkThreshold() {
        return USED_SIZE / Float.valueOf(CAPACITY) > threshold;
    }

    /**
     * 當元素超過閥值,重新擴容
     */
    private void resize() {
        int oldCapacity = CAPACITY;
        CAPACITY = 2 * CAPACITY;
        Node<K, V>[] oldNodes = nodes;
        nodes = new Node[CAPACITY];
        SIZE = 0;
        for (int i = 0; i < oldCapacity; i++) {
            Node<K, V> oldHeadNode = oldNodes[i];
            if (oldHeadNode == null) {
                continue;
            }
            System.out.println("重新擴容,遷移數組上的數據");
            this.put(oldHeadNode.getKey(), oldHeadNode.getValue());

            System.out.println("重新擴容,開始遷移鏈表上的數據");
            for (; ; ) {
                Node<K, V> oldNextNode = oldHeadNode.nextNode;
                if (oldNextNode == null) {
                    break;
                }
                if (oldNextNode != null) {
                    this.put(oldNextNode.getKey(), oldNextNode.getValue());
                }
                oldHeadNode = oldNextNode;
            }
        }
    }

    private static class Node<K, V> {
        private K key;
        private V value;
        private Node<K, V> nextNode;

        public K getKey() {
            return key;
        }

        public void setKey(K key) {
            this.key = key;
        }

        public V getValue() {
            return value;
        }

        public void setValue(V value) {
            this.value = value;
        }
    }

    /**
     * 計算元素所在的數組下標
     *
     * @param key
     * @return
     */
    private int getIndex(K key) {
        int hashCode = key.hashCode();
        if (hashCode < 0) {
            hashCode = -hashCode;
        }
        return hashCode % CAPACITY;
    }

    public int getSIZE() {
        return SIZE;
    }
}

測試用例:

package www.dustin.li.collections;

public class MyHashMapTest {

    public static void main(String[] args) {
        MyHashMap<String, String> myHashMap = new MyHashMap();
        for (int i = 0; i < 100; i++) {
            String key = "key_" + i;
            String value = "value_" + i;
            myHashMap.put(key, value);
        }
        for (int i = 0; i < 100; i++) {
            String key = "key_" + i;
            System.out.println("查詢測試,i=" + i + "  value=" + myHashMap.get(key));
        }
        for (int i = 0; i < 100; i++) {
            String key = "key_" + i;
            System.out.println("刪除前,size="+myHashMap.getSIZE());
            System.out.println("刪除測試,i=" + i + "  value=" + myHashMap.remove(key));
            System.out.println("刪除後,size="+myHashMap.getSIZE());
        }
    }
}

總結:

功能雖然簡單,並沒有一次性寫對。經過幾次測試才實現正常的邏輯。對自己邏輯思維有一定的鍛鍊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章