模仿写一个hashmap简化版:理解HashMap死锁

2020-05-13  本文已影响0人  阡陌笙箫

原谅人家写的资料我看不懂,有兴趣看看:JAVA HASHMAP的死循环

该写的注释已经都写在的代码里面,直接拷贝就能测

package com.lw.vrv.javacore.hashmap;

import java.util.Map;
import java.util.Objects;

/**
 * 自定义HashMap: 实现简单put和get方法,方便理解hashcode 用取模算出,key用int方便计算模,取模结果就是数组的下标,即key=value所要存放的位置
 * 
 * 
 * 创建类:TODO
 * 
 * @author BruceLiu
 *
 * @param <K>
 * @param <V>
 */
public class MyHashMap<K, V> {

    static final Entry<?, ?>[] EMPTY_TABLE = {};
    transient Entry<K, V>[] table = (Entry<K, V>[]) EMPTY_TABLE;
    int capacity;

    public static void main(String[] args) {
        MyHashMap<Integer, Object> myHashMap = new MyHashMap<>(2);//初始化容量为2
        
        myHashMap.put(1, "v1");
        myHashMap.put(2, "v2");
        myHashMap.put(3, "v3");
        myHashMap.put(5, "v5");
        
        //key%2 =   2     1,3
        //array:   [0]    [1]
        //linked:  v2     v5 //后进来的放最前面 
        //                v3 
        //                v1
        //
        System.out.println(myHashMap);
        
        System.out.println(myHashMap.get(2));//链表上就一条
        System.out.println(myHashMap.get(1));//链表上三条,分表是5,3,1
        System.out.println(myHashMap.get(3));
        System.out.println(myHashMap.get(5));
        
        myHashMap.resize(4);
    }

    public MyHashMap(int capacity) {
        this.capacity = capacity;
        table = new Entry[capacity]; 
    }

    public void put(K key, V value) {
        //int index = key.hashCode() % capacity;// bluckIndex
        
        int index = (Integer)key % capacity;// 这样简单点,能一眼看出来在哪个位置
        
        /**
         * 取出当前下标位置的Entry,作为下一个Entry
         * 第一次:currentEntry = null,因为默认是空数组,没有数据
         * 第二次:如果index下标位置有数据:取出当前下标的Entry对象,作为当前key-value的下一个节点(新数据替换老数据)
         * 第三次:同第二次
         * ... ...
         */
        Entry<K, V> nextEntry = table[index]; 

        table[index] = new Entry<>(index, key, value, nextEntry);
    }

    public Entry<K, V> get(K key) {
        int index = key.hashCode() % capacity; // bluckIndex
        for (Entry<K, V> e = table[index]; e != null; e = e.next) {
            Object k;
            if (e.hash == index && ((k = e.key) == key || (key != null && key.equals(k))))
                return e;
        }
        return null;
    }
    
    /**
     * 扩容方法:
     * 
     *  把原来的table数据复制到新的table上,因为索引变了,所在位置也就变了,重新赋值
     * 
     * @param capacity
     */
    public void resize(int newCapacity){
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable; //替换成新数组
    }
    
    /**
     * 
     * 关键代码:如果是自己写,你会怎么移动old table到new table
     * 
     * [0] [1] 代表数组的下标位置
     * 
     * key%2 
     * old table  [0] [1]
     *       key   2   5
     *                 3
     *                 1
     *               
     * key%4 
     * new table  [0] [1] [2] [3]
     *                 1   2   3
     *                 5      
     *                      
     */
    void transfer(Entry[] newTable) {
        int newCapacity = newTable.length;
        for (Entry<K,V> e : table) { //遍历老数组
            while(null != e) {
                
                int index = (Integer)e.getKey() % newCapacity; //重新取模 新数组下标
                
                //参考:put方法
                Entry newNextEntry = newTable[index];
                newTable[index] = new Entry<>(index, e.getKey(), e.getValue(), newNextEntry);
                
                //错误写法:因为在新的table中,下一个节点不一定还是在下一个节点,需要重新计算了,直接赋值下一个节点就是错误的
                //newTable[index] = new Entry<>(index, e.getKey(), e.getValue(), e.getNext());
                
                //获取下个节点,继续遍历
                e = e.getNext();
                
                //源码 ( 逻辑是一样的,就是代码精简一点,你品,你细品 )
                //Entry<K,V> next = e.next; //next为临时对象,保存老数据
                //e.next = newTable[index]; //相当于重新声明一个新table的下一个节点:相当于Entry next = newTable[index];
                //newTable[index] = e; //此处就是为了不重新new对象,复用对象e: 相当于newTable[index] = new Entry<>(index, e.getKey(), e.getValue(), newNextEntry);
                //e = next; //准备下一次循环:将next作为下一次遍历的对象e
               
            }
        }
    }
    
    

    /**
     * Entry对象:就是table[i]所要存放的对象,因为对象里面包含next节点,所以当作单链式结构(因为没有定义前一个节点信息)
     * 
     * 创建类:TODO
     * @author BruceLiu
     *
     * @param <K>
     * @param <V>
     */
    static class Entry<K, V> implements Map.Entry<K, V> {
        final K key;
        V value;
        Entry<K, V> next;
        int hash;

        /**
         * Creates new entry.
         */
        Entry(int h, K k, V v, Entry<K, V> n) {
            value = v;
            next = n;
            key = k;
            hash = h;
        }

        public final K getKey() {
            return key;
        }

        public final V getValue() {
            return value;
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }
        
        public Entry<K, V> getNext() {
            return next;
        }

        public void setNext(Entry<K, V> next) {
            this.next = next;
        }

        public final String toString() {
            return getKey() + "=" + getValue()+", next:"+next;
        }
        
        public final int hashCode() {
            return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
        }
    }
}

上一篇下一篇

猜你喜欢

热点阅读