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

原谅人家写的资料我看不懂,有兴趣看看: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());
        }
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355