HashMap 无显式赋值容量大小初始化,默认的 16 是在 new 的过程中,还是在第一次 put 的时候? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
MatthewHan
V2EX    Java

HashMap 无显式赋值容量大小初始化,默认的 16 是在 new 的过程中,还是在第一次 put 的时候?

  •  
  •   MatthewHan 2020-03-11 18:08:32 +08:00 3441 次点击
    这是一个创建于 2047 天前的主题,其中的信息可能已经有所发展或是发生改变。

    《码出高效》中有这么一句话:HashMap 容量并不会在 new 的过程中分配,而是在第一次 put 的时候完成创建。

    文中的源码环境是 JDK11。

    我在本地环境 JDK8 的代码里这样写:

    // 未显式初始化容量大小 Map<String, String> map = new HashMap<>(); Class<?> mapClazz = map.getClass(); Method capacity = mapClazz.getDeclaredMethod("capacity"); capacity.setAccessible(true); System.out.println("不显式的初始化,容量大小为:" + capacity.invoke(map)); 

    输出的结果为:不显式的初始化,容量大小为:16。

    我以为会和ArrayList一样,未显式初始化,容量大小是 0,只有调用一次 add 方法后,才会扩容成默认值的容量大小。

    那《码出高效》这句话该怎么解释好呢?是 JDK 版本的问题吗?反射的是capacity()方法。

    17 条回复    2020-03-13 08:36:19 +08:00
    daimazha
        1
    daimazha  
       2020-03-11 18:22:02 +08:00
    这里指的是 table
    az467
        2
    az467  
       2020-03-11 18:26:02 +08:00
    public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    Lonely
        3
    Lonely  
       2020-03-11 18:26:43 +08:00 via iPhone
    那就话应该是说内部的 node 数组是在第一次 put 的时候初始化。这种问题,你点进去源码看两眼不就很清楚了。
    hhhsuan
        4
    hhhsuan  
       2020-03-11 18:29:00 +08:00 via Android
    容量大小是 16,但空间未必已经分配了,这是两码事
    asche910
        5
    asche910  
       2020-03-11 19:23:04 +08:00
    源码 put 的时候,如果未初始化,会首先调用一次 resize 方法
    coer
        6
    coer  
       2020-03-12 01:48:55 +08:00 via Android
    打断点看构造函数,这样怎么看的出来啊
    sumulige
        7
    sumulige  
       2020-03-12 03:58:20 +08:00 via iPhone
    @hhhsuan new 的过程不是初始化 初始化不就是分配了内存空间 还是起初哪个 16 只是个值并没有完成实例化
    afpro
        8
    afpro  
       2020-03-12 09:59:06 +08:00
    ```
    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);
    }
    ```

    显然只初始化了一个 float 和一个 int

    ```
    public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
    }

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
    boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;
    ...
    }
    ```

    put 的时候如果 (tab = table) == null 会去 resize

    ```
    final Node<K,V>[] resize() {
    ...
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    ...

    ```

    在 resize 里面 new 了新的 table
    afpro
        9
    afpro  
       2020-03-12 09:59:38 +08:00
    emmm 我尝试用 ``` 放代码 但是没成功 这个怎么样才能正确的贴代码?
    MatthewHan
        10
    MatthewHan  
    OP
       2020-03-12 10:08:54 +08:00
    @afpro #8 我的代码里不是没 put 吗
    afpro
        11
    afpro  
       2020-03-12 10:13:19 +08:00
    @MatthewHan 你是没 put 啊 所以只初始化了两个参数 没有 new table 你的疑惑在哪呢??
    afpro
        12
    afpro  
       2020-03-12 10:14:37 +08:00
    @MatthewHan 你疑惑的是 capacity 这个方法??

    final int capacity() {
    return (table != null) ? table.length :
    (threshold > 0) ? threshold :
    DEFAULT_INITIAL_CAPACITY;
    }

    这不是显然 table 为 null 的时候返回了 threshold 或者 DEFAULT_INITIAL_CAPACITY

    有发帖和跟帖的时间瞄一眼 code 可好。。
    MatthewHan
        13
    MatthewHan  
    OP
       2020-03-12 10:21:50 +08:00
    @afpro #12 所以我不是问这句话怎么理解比较好,不是说我没看源码。。。
    hhhsuan
        14
    hhhsuan  
       2020-03-12 10:35:03 +08:00
    @sumulige #7 没有分配那 16 个空位,直到第一次存数据的的时候才会。
    Angzk3348
        15
    Angzk3348  
       2020-03-12 12:18:14 +08:00
    先来看看
    new HashMap<>();
    只是初始化了 loadFactor 的值.
    ====源码====
    public HashMap() {
    this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    ====源码====


    再来看看
    capacity()
    因为调用的是 无参构造器.
    table 肯定是 null 第一个三目 是 false.
    threshold 没赋值 第二个三目 也是 false.
    ====源码====
    final int capacity() {
    return (table != null) ? table.length : (threshold > 0) ? threshold :DEFAULT_INITIAL_CAPACITY;
    }
    ====源码====

    那么 再来看看
    DEFAULT_INITIAL_CAPACITY
    ====源码====
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    ====源码====
    Aresxue
        16
    Aresxue  
       2020-03-12 16:40:19 +08:00
    默认在 class 中,一直都是 16(常量),你 new 的时候只是建立了引用,put 的时候才会真正分配内存空间
    ShellMings
        17
    ShellMings  
       2020-03-13 08:36:19 +08:00 via iPhone
    码出高效 看看就好 呵呵
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2701 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 14:04 PVG 22:04 LAX 07:04 JFK 10:04
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86