简易HashMap实现

为了更好的理解HashMap线程不安全的根源,这里提供了HashMap的简易实现:

 

package map.test;
import org.apache.commons.lang3.StringUtils;

/**
 * Author:yepei@meituan.com
 * Date:2017/7/5
 * Time:15:45
 * ------------------------------------
 * Desc:
 */
public class HashMp {
    private transient Node[] entries;//数组
    private transient float loadFactor;
    private transient int size;

    public static void main(String[] args) {
        HashMp map = new HashMp(2, 0.75f);
        map.put(3, "A");
        System.out.println(map);
        map.put(5, "B");
        map.put(7, "C");
        map.put(11, "D");
        map.put(9, "E");
        map.put(12, "F");
        map.put(17, "G");
        map.put(22, "H");
        map.put(24, "I");
        map.put(25, "I");
        System.out.println(map);

        String s1 = map.get(22);
        String s2 = map.get(25);
        String s3 = map.get(0);
    }

    public HashMp(int capacity, float loadFactor) {
        this.entries = new Node[capacity];
        this.loadFactor = loadFactor;
    }

    public HashMp() {
        this(16, 0.75f);
    }

    public String put(int key, String value) {
        Node n = new Node(key, value);
        //算Hash值,为简单起见,hash值
        int i = n.getIndex(entries.length);
        Integer thisKey = key;
        //查找是否有重复: hashCode相等,且equals方法相等的
        for (Node old = entries[i]; old != null; old = old.next) {
            Integer oldKey;
            //hash相同,且equals返回true,证明有重复值插入,直接替换掉旧值
            if (old.getHash() == n.getHash() && ((oldKey = old.key) == key || thisKey.equals(oldKey))) {
                String oldValue = old.value;
                old.value = value;//替换成新值
                return oldValue;//返回旧值
            }
        }
        //该key不存在,需要增加一个结点
        addEntry(n);
        return null;
    }

    public String get(int key) {
        int idx = new Node(key, null).getIndex(entries.length);
        Integer thisKey = key;
        //查找是否有重复: hashCode相等,且equals方法相等的
        for (Node old = entries[idx]; old != null; old = old.next) {
            Integer oldKey = old.key;
            if (thisKey == oldKey || thisKey.equals(oldKey)) {
                return old.value;
            }
        }
        return null;
    }

    private void addEntry(Node n) {
        int oldLen = entries.length;
        int targetIdx = n.getIndex(oldLen);
        n.next = entries[targetIdx];
        entries[targetIdx] = n;//插入到链首
        if (size++ >= oldLen * loadFactor) {
            resize(2 * oldLen);
        }
    }

    private void resize(int newCapacity) {
        Node[] old = entries;
        Node[] newMap = new Node[newCapacity];
        for (int j = 0; j < old.length; j++) {
            Node e = old[j];
            if (e == null) {
                continue;
            }
            old[j] = null;//老表中的元素置为Null
            
            //注意:该过程会将同一个bucket中的元素逆置,因为总是将元素插在了新表的头部。
            //但不是就地逆置,因为将元素逐个copy到了新的链表中
            do {
                Node next = e.next;//暂存尾链表(去掉head的链表)
                int i = e.getIndex(newCapacity);//得到将在新表中存储的位置
                e.next = newMap[i];//切断指针,重新指向新表的表头(可能为null)
                newMap[i] = e;//将e插入到新链表头部
                e = next;//重新将e赋值为尾链表的头,对尾链表继续“切断指针,插入新表头”
            } while (e != null);
        }
        entries = newMap;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("[");
        int i = 0;
        for (Node n : entries) {
            sb.append(i++).append("{").append(n).append("}, ");
        }
        return StringUtils.removeEnd(sb.toString(), ", ") + "]";
    }

    static class Node {
        int key;
        String value;
        Node next;

        Node(int key, String value) {
            this.key = key;
            this.value = value;
        }

        int getIndex(int tableLength) {
            return getHash() % tableLength;
        }

        //为方便起见,hash值直接取该对象的key
        int getHash() {
            return key;
        }

        @Override
        public String toString() {
            return "(" + key + "," + value + ")——>" + next;
        }
    }
}

  

参考

HashMap源码解读:http://www.xiaomager.com/category/program/java/hashmap

Hashcode生成原理:http://www.cnblogs.com/godtrue/p/6395098.html

HashMap存在的三大并发问题:https://my.oschina.net/xianggao/blog/393990

posted @ 2017-07-05 17:28  一人浅醉-  阅读(470)  评论(0编辑  收藏  举报