手写HashMap

为了便于进行深刻了解HashMap的底层实现,所以手写了一个可以put和get值的简易版本的HashMap,代码如下:


/**
 * 手写HashMap
 * <p>
 *    为什么使用红黑树,不使用平衡二叉树和链表?
 *    答:因为平衡二叉树查询效率虽高,但插入数据慢。链表查询速度低,插入速度快。红黑树就是基于两者之间,查询效率
 *        和插入效率都适合。
 *    为什么JDK1.8之后的hashcode方法改变了?
 *    答:因为引入了红黑树,在hash冲突的时候,红黑树可以做到查询和插入的效率适中,那么这个时候hashcode的算法就不需要
 *        太复杂。
 *    在JDK1.8进行扩容的时候,只是判断了是否大于阈值,但在JDK1.7的时候,还多了一个判断,那就是当前节点的值是否为null.
 *    在JDK1.8的时候,就将节点加在了尾节点。1.7的时候是加在头节点的。
 *    在JDK1.8的时候,扩容导致的死锁问题也得到了解决,就是通过:
 *    newtab[j + old] = hihead;
 *    ConcurrentModificationException异常?
 *    因为在遍历hashmap的时候删除元素,会可能造成线程安全问题。报上述的错,原因是modcount!=expectModCount
 *
 *    注意:Person p1 = new Person();Hashmap ha = new Hashmap(): ha.put(p1,"11");
 *    ha.get(p1);  p1 = new Person();  ha.get(p1);
 *    那么第一个ha.get(p1)就会返回11,第二个返回为null。所以一般把p1设置为final修饰。
 *
 *
 *
 * </p>
 *
 * @author lh
 * @version 1.0
 * @date 2020-04-07 17:04
 */
public class MyHashMap<K,V>{

    // 定义Entry数组
    private Entry[] table = null;
    // 定义集合元素个数
    private Integer index = 0;
    private static final int MAXIMUM_CAPACITY = 1 << 30;

    // 初始化集合元素个数
    private static final Integer CAPACITY = 8;

    public MyHashMap() {
        table = new Entry[CAPACITY];
    }

    public MyHashMap(Integer size) {
        if(size == null){
            throw new NullPointerException();
        }
        // 重新调整hashcode,确保数组长度为2的次方
        int tableSizeFor = tableSizeFor(size);
        table = new Entry[tableSizeFor];
    }

    public int size() {
        return index;
    }


    /**
     * 根据key获取值
     * @param key
     * @return
     */
    public Object get(Object key) {
        int i = key.hashCode();
        int i1 = i % CAPACITY;
        for (Entry<K, V> entry = table[i1]; entry != null; entry = entry.next) {
            if (entry.getK().equals(key)) {
                return entry.v;
            }
        }
        return null;
    }

    /**
     * 将输入的参数处理为2的倍数,如传入的是:15,那么实际初始化是16.
     * 因为这里会执行一个操作(JDK1.7):indexFor
     * ...........................JDK1.7............................
     * 逻辑是:hashcode & (length-1)
     * hashcode: 0010 0110
     * length-1: 0000 1111
     * hashcode & (length-1) : 0000 1111 也就是15
     * ...........................JDK1.8............................
     * JDK1.8处理上面的逻辑合并在了putVal方法:如
     * if ((tab = table) == null || (n = tab.length) == 0)
     *             n = (tab = resize()).length;
     * if ((p = tab[i = (n - 1) & hash]) == null)
     *             tab[i] = newNode(hash, key, value, null);
     * 关键代码:(n - 1) & hash 其作用就是:hashcode & (length-1)
     *
     * 但是如果hashcode为:1101 0110 那么hashcode & (length-1) 结果还是15
     * 所以这里要进行右移操作
     * (h = key.hashCode()) ^ (h >>> 16)
     *
     *
     * @param cap
     * @return
     */
    static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }


    /**
     * 插入值
     * @param key
     * @param value
     * @return
     */
    public V put(K key, V value) {
        int i = key.hashCode();
        int i1 = i % CAPACITY;
        for (Entry<K, V> entry = table[i1]; entry != null; entry = entry.next) {
            if (entry.getK().equals(key)) {
                V old = entry.v;
                entry.setV(value);
                return old;
            }
        }
        addEntry(key, value, i1);
        index++;
        return null;

    }

    /**
     * 向hashMap插入值
     * @param key
     * @param value
     * @param i1
     */
    private void addEntry(K key, V value, int i1) {
        Entry entry = new Entry(key, value, table[i1]);
        table[i1] = entry;
    }

    class Entry<K, V> {
        public K k;
        public V v;
        public Entry next;

        public Entry(K k, V v, Entry next) {
            this.k = k;
            this.v = v;
            this.next = next;
        }

        public K getK() {
            return k;
        }

        public void setK(K k) {
            this.k = k;
        }

        public V getV() {
            return v;
        }

        public void setV(V v) {
            this.v = v;
        }

        public Entry getNext() {
            return next;
        }

        public void setNext(Entry next) {
            this.next = next;
        }
    }

    public static void main(String[] args) {
        MyHashMap<String, String> myHashMap = new MyHashMap();
        int i = myHashMap.tableSizeFor(30);
        System.out.println("1-="+i);

    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值