HashMap源码学习

HashMap是Java中非线程安全的散列表,采用数组+链表+红黑树实现。当链表长度超过8时,转换为红黑树以优化查找效率。在put、get和remove等操作中,HashMap通过key的hash值定位元素位置,并处理碰撞。resize方法用于自动扩容,当元素数量达到阈值时,数组大小翻倍。在删除操作中,HashMap会根据key找到节点并移除。
摘要由CSDN通过智能技术生成

HashMap源码学习

简介

HashMap是数组+链表实现,采用key/value的键值对,每个key对应唯一的value,查询和修改的效率很快,能达到O(1)的平均时间复杂度,是非线程安全的且不能保证元素的存储的顺序。

存储结构

采用数组+链表,出现hash冲突的时采用链表解决hash冲突。hashmap定义了一个数组变量transient Node<K,V>[] table;Node是一个静态内部类实现了Map.Entry<K,V>,采用链表指向下一个节点。

        final int hash;
        final K key;
        V value;
        Node<K,V> next;

hashmap
HashMap采用数组+链表+红黑树,一个数组下标位存储Node链表。在添加元素时会根据key计算出hash值算出在数组的下标位。
当链表长度超多了8时会转化为红黑树并再元素减少到6时候又会把红黑树转为链表来提高效率。数组的查询效率O(1),链表是O(n),红黑树的查询效率O(log n)。

属性变量

/**
 * 默认数组长度为16,HashMap空构造函数时第一次扩容的时候用
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

/**
 * 最大的容量为2的30次方,数组长度的最大值
 */
static final int MAXIMUM_CAPACITY = 1 << 30;

/**
 * 默认的装载因子,确定容量达到多少时进行扩容。
 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;

/**
 * 当一个数组的链表长度大于等于8时转化为红黑树
 */
static final int TREEIFY_THRESHOLD = 8;

/**
 * 当一个数组的链表是红黑树结构但后续删减之后小于等于6时把红黑树转为普通链表
 */
static final int UNTREEIFY_THRESHOLD = 6;

/**
 * 当数组的长度大于
 */
static final int MIN_TREEIFY_CAPACITY = 64;

/**
 * 数组,又叫作桶(bucket)
 */
transient Node<K,V>[] table;

/**
 * 作为entrySet()的缓存
 */
transient Set<Map.Entry<K,V>> entrySet;

/**
 * 元素的数量
 */
transient int size;

/**
 * 修改次数,用于在迭代的时候执行快速失败策略
 */
transient int modCount;

/**
 * 当桶的使用数量达到多少时进行扩容,threshold = capacity * loadFactor
 */
int threshold;

/**
 * 装载因子
 */
final float loadFactor;

内部类

HashMap的数组是Node数组,Node是一个典型的单链表节点,其中,hash用来存储key计算得来的hash值。

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

        Node(int hash, K key, V value, Node<K,V> next) {
   
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

上面提到当链表会转化为红黑树,HashMap定义了TreeNode的静态内部类,是一个树形结构。定义如下

    static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
   
        TreeNode<K,V> parent;  // red-black tree links
        TreeNode<K,V> left;
        TreeNode<K,V> right;
        TreeNode<K,V> prev;    // needed to unlink next upon deletion
        boolean red;
    }

构造方法

空构造方法,属性使用默认值

    public HashMap() {
   
        this.loadFactor = DEFAULT_LOAD_FACTOR; //指定扩容因子0.75
    }

指定容器大小和扩容因子的构造函数,HashMap(int initialCapacity)也是调用这个函数

    public HashMap(int initialCapacity, float loadFactor) {
   
        // 检查传入的初始容量是否合法
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        // 检查装载因子是否合法
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        this.threshold = tableSizeFor(initialCapacity);
    }

put(K key, V value)

添加元素

    public V put(K key, V value) {
   
        //调用hash(key)计算key的hash值
        return putVal(hash(key), key, value, false, true);
    }
    static final int hash(Object key) {
   
        int h;
        //若key为null,则hash值为0;否则使用key的hashcode获取hash并让hash和高位的16位异或获取hash值
        //int类型4个字节32位,相当于让高16位和低16位异或,保证计算出的hash更分散。
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    //往容器添加元素
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
   
        //tab数组临时变量,p用于链表查找,n记录数组长度,i数字索引临时变量。
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        //当数组位空或数组长度为0时,进行扩容范围新的数组长度
        if ((tab = table) == null || (n = tab.length) == 0)
            //调用resize进行扩容
            n = 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值