⭐阿里面试官叫我手写HashMap⭐,我两分钟就给他整出来了!!!


本文作者: Code皮皮虾,CSDN、掘金等各大平台同名,有兴趣的小伙伴可以点一波关注😁,感谢您的支持!
⭐持续分享大厂面试题⭐
公众号:JavaCodes


前言

今天看面经看得到大厂面试题,说实话HashMap感觉真的很了解,源码看了也很多遍了,相信大部分小伙伴都能有这个程度。但是,突然给你来这么一手,还是有点懵圈!所以,今天给各位小伙伴整理一下,帮助各位掌握!


正常来说,面试时遇到手写HahsMap,基本上都是要求实现get、put方法,我就稍微全面那么一点,再加一个remove方法。

JDK7、8HashMap的get()、put()方法流程

JDK7、8HashMap扩容详解

<font size="5">==重点掌握==

手写LRU、LFU,让面试官对你刮目相看!!!

HashMap在JDK7、JDK8中不安全的地方到底在哪里???


好了好了,话不多说,我们开始吧。


撸起袖子开始造

实现数组 + 链表

众所周知,HashMap无论是JDK7还是JDK8,底层都是数组 + 链表,只是JDK8多了一个红黑树,按道理讲不会还有手写红黑树的吧,😰。

既然是数组 + 链表,那我就实现一个链表

参考==JDK8==源码

在这里插入图片描述

我们就没必要那么高级,🤭

static class Node {
    int key, value; //保存该节点的Key、Value
    Node next; //指向下一个节点

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

链表有了,就再来个数组(面试过程基本上不要求扩容,所以我们就直接给数组定义一个差不多的值就OK了)

private final int CAPACITY = 10000;
Node[] nodes = new Node[CAPACITY];



实现获取Key对应数组索引的方法

参考源码

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}


final Node<K,V> getNode(int hash, Object key) {
   Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
   if ((tab = table) != null && (n = tab.length) > 0 &&
       (first = tab[(n - 1) & hash]) != null) {
       if (first.hash == hash && // always check first node
           ((k = first.key) == key || (key != null && key.equals(k))))
           return first;
       if ((e = first.next) != null) {
           if (first instanceof TreeNode)
               return ((TreeNode<K,V>)first).getTreeNode(hash, key);
           do {
               if (e.hash == hash &&
                   ((k = e.key) == key || (key != null && key.equals(k))))
                   return e;
           } while ((e = e.next) != null);
       }
   }
   return null;
}

图示讲解(温馨提示:看不清的可以鼠标右键在新标签页打开图片哦)

在这里插入图片描述

实现

因为int为基本数据类型,所以我们用==Integer.hashCode(int value)==

而Integer.hashCode(int value),返回的其实就是你传入的值

public static int hashCode(int value) {
    return value;
}
private int getIndex(int key) {
    int hash = Integer.hashCode(key);
    //高16位异或低16位
    hash ^= (hash >>> 16);
    //与数组长度取模,得到对应的索引下标
    return hash % CAPACITY;
}



实现get方法

流程很简单

  1. 获取到传入的 key 的对应的索引下标
  2. 拿到对应下标对应的链表首节点
  3. 非空判断
  4. 如果链表首节点的Key是目标Key,那么直接返回对应的Value值;如果不是,那么对链表进行遍历获取,如果遍历完成都没有去返回Value值,那么说明HashMap没有这个数据,那么就返回-1.
public int get(int key) {
    int idx = getIndex(key);
    Node now = nodes[idx];

    if (now != null) {
        if (now.key == key) {
            return now.value;
        } else {
            while (now != null) {
                if (now.key == key) {
                    return now.value;
                }
                now = now.next;
            }
        }
    }

    return -1;
}

参考源码

final Node<K,V> getNode(int hash, Object key) {
 Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        
        //如果是首节点,那么直接返回
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            //红黑树判断不用管
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            //遍历获取
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    //如果还没有就返回null值
    return null;
}



实现put方法

流程介绍

注意:我们需要保存前一个节点,这样如果put的是一个新键值对的话,我们可以获取到链表的最后一个不为null的节点

  1. 获取Key对应的索引值
  2. 非空判断,如果为空,说明该索引对应的链表为空,可直接创建新节点添加
  3. 不为空则循环遍历,遍历过程更新 prev ,如果遍历过程中找到则返回value值
  4. 如果遍历完成还没有返回,说明没有该节点可以添加,那么根据 prev 是否为null进行添加;
public void put(int key, int value) {
    int idx = getIndex(key);
    Node now = nodes[idx], tmp = now;
    
    if (tmp != null) {
        Node prev = null;
        while (tmp != null) {
            if (tmp.key == key) {
                tmp.value = value;
                return;
            }
            prev = tmp;
            tmp = tmp.next;
        }
        tmp = prev;
    }

    Node node = new Node(key, value);
    if (tmp != null) {
        tmp.next = node;
    } else {
        nodes[idx] = node;
    }
}



实现remove方法

大致流程跟get方法差不多,区别就是我们我们需要==保存需要删除节点的前一个节点==

 public void remove(int key) {
     //得到索引
     int idx = getIndex(key);
     //拿到首节点
     Node now = nodes[idx];

     //非空判断
     if (now != null) {
         //保存前节点
         Node prev = null;
         //遍历查找
         while (now != null) {
             //如果找到
             if (now.key == key) {
                 //这里有两种情况
                 //1. 如果要删除的节点是首节点,那么直接让当前数组下标对应的首节点位为其下一个节点
                 //2. 如果不是,那么让前一个节点的下一个节点指向当前要删除节点的下一个节点就实现了删除效果
                 if (prev != null) {
                     prev.next = now.next;
                 }else {
                     nodes[idx] = now.next;
                 }
                 //不管是怎么删除的,都让当前节点的下一个节点为null,方便垃圾挥手(加分点哦)
                 now.next = null;
                 return;
             }
             //如果没找到,让前节点指向当前节点,当前节点指向其下一个节点
             prev = now;
             now = now.next;
         }
     }
 }



测试一下

public static void main(String[] args) {
    MyHashMap map = new MyHashMap();
    map.put(1,1);
    map.put(2,2);
    map.put(1,40);
    map.put(2,200);

    System.out.println(map.get(1));
    System.out.println(map.get(2));
}
在这里插入图片描述



完整代码

public class MyHashMap {

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

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

    private final int CAPACITY = 10000;
    Node[] nodes = new Node[CAPACITY];

    public void put(int key, int value) {
        int idx = getIndex(key);
        Node now = nodes[idx], tmp = now;
        if (tmp != null) {
            Node prev = null;
            while (tmp != null) {
                if (tmp.key == key) {
                    tmp.value = value;
                    return;
                }
                prev = tmp;
                tmp = tmp.next;
            }
            tmp = prev;
        }

        Node node = new Node(key, value);
        if (tmp != null) {
            tmp.next = node;
        } else {
            nodes[idx] = node;
        }
    }

    public int get(int key) {
        int idx = getIndex(key);
        Node now = nodes[idx];

        if (now != null) {
            if (now.key == key) {
                return now.value;
            } else {
                while (now != null) {
                    if (now.key == key) {
                        return now.value;
                    }
                    now = now.next;
                }
            }
        }

        return -1;
    }

    public void remove(int key) {
        int idx = getIndex(key);
        Node now = nodes[idx];

        if (now != null) {
            Node prev = null;
            while (now != null) {
                if (now.key == key) {
                    if (prev != null) {
                        prev.next = now.next;
                    }else {
                        nodes[idx] = now.next;
                    }
                    now.next = null;
                    return;
                }
                prev = now;
                now = now.next;
            }
        }
    }

    private int getIndex(int key) {
        int hash = Integer.hashCode(key);
        hash ^= (hash >>> 16);
        return hash % CAPACITY;
    }

}




最后

我是 Code皮皮虾,一个热爱分享知识的 皮皮虾爱好者,未来的日子里会不断更新出对大家有益的博文,期待大家的关注!!!

创作不易,如果这篇博文对各位有帮助,希望各位小伙伴可以==点赞和关注我哦==,感谢支持,我们下次再见~~~

==分享大纲==

大厂面试题专栏


Java从入门到入坟学习路线目录索引


开源爬虫实例教程目录索引


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

推荐阅读更多精彩内容