【Java】20211019_Java 集合 _HashMap(JDK1.7)

【Java】20211019_Java集合_HashMap(JDK1.7)


一、链表

链表是不连续的内存空间,单向链表之间通过持有下一节点的内存地址来指向。

链表.png


二、HashMap的底层结构

JDK1.7的HashMap底层结构(见代码块2-1):数组+链表;

[^_代码块2-1]

/**
 * The table, resized as necessary. Length MUST Always be a power of two.
 * 根据需要对数组table扩容,但需要保证数组的长度始终是2的幂次。
 */
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;

数组的标志——"[]"有了,再看作为链表结构的Entry<K,V>(见代码块2-2):

[^_代码块2-2]

static class Entry<K,V> implements Map.Entry<K,V> {
    final K key;
    V value;
    Entry<K,V> next;    // 持有下一Entry节点的地址
    int hash;
}        

每个Entry节点中都持有下一节点的地址,一个接一个形成链表结构。


三、为什么是【数组+链表】

个人猜想:数组查询快,链表增删强,强强来联手,两者得益彰。


四、从构造方法开始,看看码

在JDK1.7,HashMap提供了四种构造,这里只贴其中三种的代码(见代码块4-1)分析。

[^_代码块4-1]

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final float DEFAULT_LOAD_FACTOR = 0.75f;

/**
 * Constructs an empty <tt>HashMap</tt> with the default initial capacity
 * (16) and the default load factor (0.75).
 * 构造一个还没有放入任何元素的HashMap,其初始化容量为16,默认的加载因子0.75
 */
public HashMap() {
    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);
}

public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

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;
    threshold = initialCapacity;
    init();
}   

如果在创建HashMap时,没有指定初始化容量,就会默认初始化一个容量为16的HashMap。指定了容量,那就按指定的来,不过在put方法时还是会根据这个容量做调整。

在看put方法前,先看一下别的方法,省得一会儿跳来跳去看代码晕乎了。

inflateTable方法(见代码块4-2):

[^_代码块4-2]

/**
 * Inflates the table.
 */
private void inflateTable(int toSize) {
    // Find a power of 2 >= toSize
(1)    int capacity = roundUpToPowerOf2(toSize);

(2)    threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
(3)    table = new HashMap.Entry[capacity];
(4)    initHashSeedAsNeeded(capacity);
}

static final int MAXIMUM_CAPACITY = 1 << 30; // 1073741824->不用数了,10个亿多

private static int roundUpToPowerOf2(int number) {
    // assert number >= 0 : "number must be non-negative";
    return number >= MAXIMUM_CAPACITY
            ? MAXIMUM_CAPACITY
            : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
}

(1)roundUpToPowerOf2方法整体看下来,【较大概率】只会发生一件事:将传入的任意值,通过运算,返回一个【大于等于】传入值又在数值上最接近传入值的2次幂数。

(2)这行代码【较大概率】会将上一步的返回值与0.75做乘法,将得到的值赋给threashold——中文翻译:阈值。

(3)构造一个容量为roundUpToPowerOf2方法返回值的新数组替换掉HashMap中原有数组。

(4)略。

put方法(见下图:)

HashMap(JDK1.7).jpg


努力让自己不那么菜

明天就1024了,本来今天网络冲浪的时候微信群里发现了个GIF,想分享出来。

但因为我不知道微信咋保存,所以去B站扒了个相同的视频下来想视频转GIF,但是喵了个咪的现在的工具就想着收钱,干活的时候网速飞慢,要钱的时候伸手贼快,我又比较穷,就面向搜索引擎找了个代码跑了下把GIF给生成了。

药水哥教你敲代码.gif

欲速则不达,每天进步一点点。