说明:源码的注释我写在行尾,这样即使有自己的注释也能进行调试,不会造成调试时候行数错乱的问题

背景

  • JDK1.7 HashMap数据结构:数组 + 链表
  • JDK1.8 HashMap数据结构:数组 + 链表 / 红黑树

补充

多个 key 通过散列函数会得到相同的值,这时候怎么办?

解决:

​ (1)开放地址法

​ (2)链地址法

签名

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {}

基本字段、

/**
     * The default initial capacity - MUST be a power of two.
     */
// aka 16   0001 -> 10000   默认初始化容量,这里的初始容量仅是HashMap的初始大小,并不限制HashMap可以存储的键值对数量,因为HashMap可以动态地扩容以容纳更多的元素。
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;

// 最大容量 2^30
static final int MAXIMUM_CAPACITY = 1 << 30;

   /**
     * The load factor used when none specified in constructor.
     */
// 负载因子表示当哈希表中的元素数量达到总容量的多少时,触发自动扩容的阈值。默认负载因子0.75意味着当哈希表中的元素数量达到容量的75%时,HashMap会自动进行扩容操作。
static final float DEFAULT_LOAD_FACTOR = 0.75f;

// 用于确定何时将链表转换为红黑树的阈值。static final int TREEIFY_THRESHOLD = 8; 

// 用于在调整大小(resize)操作期间对一个(拆分的)桶进行取消树化(untreeify)的阈值。
static final int UNTREEIFY_THRESHOLD = 6; 

// 进行树化的最小数组容量!(不是链表长度仅大于8就可以的)
static final int MIN_TREEIFY_CAPACITY = 64;

// map中 k,v的对数
transient int size;  

// 记录集合被修改的次数,这个字段是用于迭代器中的快速失败 ,这里的修改是结构化修改,是指改变hashmap中元素的数量
transient int modCount;

// 调整大小的下一个大小值 (capacity * load_factor)
int threshold;

putMapEntries

    /**
     * Implements Map.putAll and Map constructor
     *
     * @param m the map
     * @param evict false when initially constructing this map, else
     * true (relayed to method afterNodeInsertion).
     */
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();                       // m:要插入的map
        if (s > 0) {                            //
            if (table == null) { // pre-size    // 如果目标map的数组为空
                float ft = ((float)s / loadFactor) + 1.0F;  // 将s除以负载因子+1可以得到HashMap所需的最大负载容量
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?   // 进行边界判断
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)              // threshold:调整大小的下一个容量,如果大于就重新赋值
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)             // 如果要加入进来的数组大小很大,已经大于当前调整大小的容量, 要扩容
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

为什么某些字段用transient修饰

hashmap只希望序列化实际存储的数组,不希望将其余的元素也进行序列化
详情见文章点击

参考文献

1.HashMap源码看我这篇就够了
2.HashMap putVal解读
3.HashMap transient
4.字节超大仙的HashMap源码解读
5.HashMap hash冲突解决
6.hash冲突解决

分类: java

2 条评论

maintest · 2024-01-25 09:58

Greetings from California! I’m bored to death at work so I decided to browse your website
on my iphone during lunch break. I love the information you present here
and can’t wait to take a look when I get home. I’m shocked at how fast your blog loaded on my cell phone ..
I’m not even using WIFI, just 3G .. Anyways, awesome site!

googletest · 2024-01-26 19:55

I absolutely love your website.. Very nice colors &
theme. Did you develop this amazing site yourself?
Please reply back as I’m looking to create my own personal blog and
would like to learn where you got this from or exactly what the theme is called.
Cheers!

发表评论

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用*标注

ICP备案号: 辽ICP备20003309号