hashmap是单向链表吗_面试必备:HashMap(JDK1.8)原理以及源码分析

对于HashMap想必大家都不陌生,无论是平时code还是面试都经常和它打交道。今天我们通过源码的层面来分析一下它的实现原理,注意本文基于的是JDK1.8。

问题是从哪边开始聊起呢?我觉得不妨先从一段熟悉的代码开始。

MapString> map = map.put(1, "Jack");

然后我们会迫不及待点开HashMap这个类,发现里面有大量的属性和方法,一脸懵逼。那就直接点开put方法?点了之后发现下面这段代码。

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,                   boolean evict) {
            Node[] tab; Node p; int n, i;        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);

依旧一脸懵逼,完全没有看下去的欲望,怎么办?为什么会这样?原因是我们不了解HashMap的数据结构,什么意思?也就是说,当把key,value存储到HashMap中之后,不知道它们是以何种数据排列的方式去存储的,这样根本就不明白源码写的思路是什么,所以我们先把目光转向到数据结构,更准备地说是HashMap的数据结构。

1 HashMap数据结构

从网上的很多资料我们知道,HashMap1.7的数据结构是数组+链表,HashMap1.8的数据结构是数组+链表+红黑树,下面这张图我画出了HashMap1.8的数据结构。

53c51a4a05b985be7a180007c7fb0bcf.png

有些哥们可能会说,我在网上看到的不是这样。这时候你可以发挥空间想象能力逆时针旋转个90度,也就是下面这样的展示形式。

432af923f54a8e83d055c3a986035f23.png

不管如何,反正都能体现出是数组+链表+红黑树的数据结构的方式。虽然数据结构是知道了,但是关键是图解中的每个小格子表示的是什么呢?对于我们了解HashMap的原理和源码有什么作用吗?先别急,一个个来看,先看每个小格子表示什么。

2 每个小格子的含义

我们可以猜想一下,每个小格子表示里面至少包含了key,value,为什么这么说呢?因为hashmap.put(key,value)之后,就会形成上述的数组+链表+红黑树的结构,那这个结构中的每个小格子至少把key和value涵盖进去了,如果不是,那么key,value怎么存储呢?ok,假如这个猜想是对的,那Java中想要同时存储key,value两个值,该怎么表示呢?我觉得可以用XXX类,比如下面的伪代码。

class XXX{
        private Integer key;    private String value;}

我觉得靠谱,如果真的是这样,那么要想形成上述的数据结构的图解,只需要创建一个个XXX类的对象,然后排列好它们的方式不就ok了吗?没错,关键这个排列要形式数组+链表+红黑树的数据结构。我们暂且给XXX一个名称叫”Node”,于是就是这样了。

class Node{
        private Integer key;    private String value;}

这时候有些哥们想,上面都是你主观的一个猜想,源码中真的是这样做的吗?我们不妨在HashMap类中搜索一下”Node”,发现有这样一个内部类。

static class Node<K,V> implements Map.Entry<K,V> {
            final int hash;        final K key;        V value;        Node next;    ...}

ok,至此每个小格子表示的含义猜想和验证已经完成,发现源码中真的也有这样一个Node类,并且里面维护了key和value属性,至于其他属性是什么含义,我们后面再聊。

3 Node的排列方式/数据结构

上面既然已经验证了小格子对应的就是Node类,或者可以称为是Node节点。接下来我们的任务就是将这些节点来排列成数组+链表+红黑树的形式。

3.1 数组

想要将Node节点形式数组,按照以往的经验只需要在类中维护一个Node[]的属性即可,那么源码中是否有这样做呢?

/**    * The table, initialized on first use, and resized as    * necessary. When allocated, length is always a power of two.    * (We also tolerate length zero in some operations to allow    * bootstrapping mechanics that are currently not needed.)    */   transient Node[] table;

我们会发现源码中维护了这样一个成员变量,Node[] table,这样数组的排列方式就解决了。

3.2 链表

链表无非就是Node节点和Node节点的关系的维护,这个关系可以分为单向链表或者双向链表,在前面的图解中我们发现这个链表是单向的,但是如果要想在源码中验证这个单向链表,只需要在原来的Node类中维护一个Node属性,如下所示。

class Node{
        private Integer key;    private String value;    private Node next;    //单向链表属性的维护}

那源码中是否是这样做的呢?通过下面代码中的Node next属性可以发现的确是单向链表的方式

static class Node<K,V> implements Map.Entry<K,V> {
            final int hash;        final K key;        V value;        Node next;}

3.3 红黑树

红黑树是一种特殊的二叉树,对于二叉树我们比较熟悉,会有父节点,左子树,右子树等。

这时候我们会想,源码中是否有这样来做呢?搜索”TreeNode”,发现会有这样一段代码。

/**     * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn     * extends Node) so can be used as extension of either regular or     * linked node.     */    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
            TreeNodeparent;  // red-black tree links        TreeNode left;        Tree
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值