HashMap学习笔记

1、HashMap底层数据结构

底层数据结构:
    数组+链表+红黑树(JDK1.8以后)
1.1 数组
数组:
    使用一段连续的存储单元存储数据,对于指定下标的查找,时间复杂度为O(1),对于一般的插入删除操作,涉及到数组元素的移动,其平均复杂度为O(n)。所以说虽然数组读取速度快,但是前提是需要知道数组对应元素的下标索引。
1.2 链表
链表:
    对链表的新增,删除操作在查找到操作位置后,只需要处理节点间的引用即可,时间复杂度为O(1),查找操作需要遍历链表中的所有节点逐一进行比对,时间复杂度为O(n)。
1.3 红黑树
红黑树:
    一种接近平衡二叉搜索树,在每个节点上增加一个存储为表示节点的颜色,可以是Red或Black,通过对任何一条从根到叶子的路径上各个节点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的,
    支持查找、插入、删除等操作,其平均时间复杂度最坏为O(logn<n)
    
红黑树的5个特性:
    1. 每个节点要么是红色,要么是黑色。
    2. 根结点事黑色。
    3. 每个叶结点(叶结点即指树尾端null指针或null结点)都是黑色的。
    4. 如果一个结点是红色,那么它的两个子结点都是黑色。
    5. 对于任意结点而言,其到叶结点树尾端null指针的每条路径都包含相同数量的黑结点。

2、HashMap底层存储原理

2.1 HashMap的key为空时
HashMap中的key允许为空,当key为空的时候会单独开辟一个空间用于存储当前的值。具体实现
// JDK1.8之后HashMap中的寻址算法,其算法规则效果等同于 hash % length但是性能要更好,另外也是由于这个原因所以HashMap初始化的时候数组长度需要为2的n次方,例如HashMap创建时默认的数组长度为16。
hash & (length - 1)

// JDK1.8之后HashMap中的Hash算法的优化(h = key.hashCode()) ^ (h >>> 16)
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
2.2 HashMap取值原理
    HashMap的取值原理:由于HashMap底层是由数组 + 链表 + 红黑树组成,因此根据key取值的时候并不仅仅只比较key在HashMap底层数组对应的下标,还会比较下标对应链表/红黑树每个对象中的hashCode值,只有链表/红黑树中的对象的hashCode值与对应key的hashCode相等才能确定当前key对应的对象。底层源码实现:
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);
    }
}
2.3 HashMap链表与红黑树的转换
    HashMap底层由链表转成红黑树的阈值为:8,之所以不在刚开始就使用红黑树而使用链表,是因为链表的插入性能高于红黑树。具体底层源码实现
static final int TREEIFY_THRESHOLD = 8;

for (int binCount = 0; ; ++binCount) {
    if ((e = p.next) == null) {
        p.next = newNode(hash, key, value, null);
        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
            treeifyBin(tab, hash);
        break;
    }
    if (e.hash == hash &&
        ((k = e.key) == key || (key != null && key.equals(k))))
        break;
    p = e;
}

3、模拟HashMap自定义Map类

Map接口

package com.xununan.www.map;

/**
 * @packageName com.xununan.www.map
 * @className Map
 * @description: 自定义Map
 * @author: xu
 * @create: 2020/04/26 22:25
 */
public interface Map<K, V> {

    public V put(K k, V v);

    public V get(K k);

    public int size();

    public interface Entry<K, V> {

        public K getKey();

        public V getValue();
    }
}

Map实现:数组 + 链表的方式

package com.xununan.www.map;

/**
 * @packageName com.xununan.www.map
 * @className MyMap
 * @description: 自定义Map类
 * @author: xu
 * @create: 2020/04/26 22:27
 */
public class MyMap<K, V> implements Map<K, V> {

    private Entry[] table = null;

    private int size = 0;

    public MyMap() {
        table = new Entry[16];
    }

    /**
     * 功能描述:put <br>
     *     1. key查询hashCode
     *     2. hashCode取模获得下标
     *     3. 根据下标找到数组对象
     *     4. 是否为空进行存储
     * @param k
     * @param v
     * @return V
     *
     * @Author xu
     * @Date 2020-04-26 22:32
     * @Version 1.0.0
     */
    @Override
    public V put(K k, V v) {
        int index = this.hash(k);
        Entry<K, V> entry = table[index];
        if (entry == null) {
            table[index] = new Entry(k, v, k.hashCode(), null);
            size ++ ;
        } else {
            table[index] = new Entry(k, v, k.hashCode(), entry);
        }
        return (V) table[index].getValue();
    }

    private int hash(K k) {
        return Math.abs(k == null ? 0 : k.hashCode() % 16);
    }

    /**
     * 功能描述:get <br>
     *     1. key找到index
     *     2. 数组位置
     *     3. 判断当前对象是否有值,有的话比较对象的hashcode
     *     4. 如果当前hashcode值详单则直接返回如果不相等则继续比较下一个对象的hashcode
     * @param k
     * @return V
     *
     * @Author xu
     * @Date 2020-04-26 22:43
     * @Version 1.0.0
     */
    @Override
    public V get(K k) {
        int index = this.hash(k);
        Entry<K,V> entry = table[index];
        if (entry == null) {
            return null;
        }
        do {
            if (k.hashCode() == entry.hashCode) {
                return entry.getValue();
            } else {
                entry = entry.next;
            }
        } while (entry != null);

        return null;
//        return this.find(index, entry);
    }

    private V find(int index, Entry<K,V> entry) {
        if (entry.hashCode == index) {
            return entry.getValue();
        } else {
            if (entry.next != null) {
                return find(index, entry.next);
            } else {
                return null;
            }
        }
    }

    @Override
    public int size() {
        return size;
    }

    class Entry<K, V> implements Map.Entry<K, V> {

        K k;
        V v;
        int hashCode;
        Entry<K, V> next;

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

        @Override
        public K getKey() {
            return k;
        }

        @Override
        public V getValue() {
            return v;
        }
    }
}

以上为个人总结的一些笔记,若有雷同纯属巧合,若有错误欢迎指出,谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值