当前位置 主页 > 网站技术 > 代码类 >

    java中hashmap容量的初始化实现

    栏目:代码类 时间:2019-11-22 09:09

    HashMap使用HashMap(int initialCapacity)对集合进行初始化。

    在默认的情况下,HashMap的容量是16。但是如果用户通过构造函数指定了一个数字作为容量,那么Hash会选择大于该数字的第一个2的幂作为容量。比如如果指定了3,则容量是4;如果指定了7,则容量是8;如果指定了9,则容量是16。

    为什么要设置HashMap的初始化容量

    在《阿里巴巴Java开发手册》中,有一条开发建议是建议我们设置HashMap的初始化容量。

    下面我们通过具体的代码来了解下为什么会这么建议。

    我们先来写一段代码在JDK1.7的环境下运行,来分别测试下,在不指定初始化容量和指定初始化容量的情况下性能情况的不同。

    public static void main(String[] args) {
     int aHundredMillion = 10000000;
    
     // 未初始化容量
     Map<Integer, Integer> map = new HashMap<>();
     long s1 = System.currentTimeMillis();
     for (int i = 0; i < aHundredMillion; i++) {
      map.put(i, i);
     }
     long s2 = System.currentTimeMillis();
     System.out.println("未初始化容量,耗时: " + (s2 - s1)); // 14322
    
     // 初始化容量为50000000
     Map<Integer, Integer> map1 = new HashMap<>(aHundredMillion / 2);
     long s3 = System.currentTimeMillis();
     for (int i = 0; i < aHundredMillion; i++) {
      map1.put(i, i);
     }
     long s4 = System.currentTimeMillis();
     System.out.println("初始化容量5000000,耗时: " + (s4 - s3)); // 11819
    
     // 初始化容量为100000000
     Map<Integer, Integer> map2 = new HashMap<>(aHundredMillion);
     long s5 = System.currentTimeMillis();
     for (int i = 0; i < aHundredMillion; i++) {
      map2.put(i, i);
     }
     long s6 = System.currentTimeMillis();
     System.out.println("初始化容量为10000000,耗时: " + (s6 - s5)); // 7978
    }
    
    

    从以上的代码不难理解,我们创建了3个HashMap,分别使用默认的容量(16)、使用元素个数的一半(5千万)作为初始容量和使用元素个数(一亿)作为初始容量进行初始化,然后分别向其中put一亿个KV。

    从上面的打印结果中可以得到一个初步的结论:在已知HashMap中将要存放的KV个数的时候,设置一个合理的初始化容量可以有效地提高性能。下面我们来简单分析一下原因。

    我们知道,HashMap是有扩容机制的。所谓的扩容机制,指的是当达到扩容条件的时候,HashMap就会自动进行扩容。而HashMap的扩容条件就是当HashMap中的元素个数(Size)超过临界值(Threshold)的情况下就会自动扩容。

    threshold = loadFactor * capacity

    在元素个数超过临界值的情况下,随着元素的不断增加,HashMap就会发生扩容,而HashMap中的扩容机制决定了每次扩容都需要重建hash表,这一操作需要消耗大量资源,是非常影响性能的。因此,如果我们没有设置初始的容量大小,HashMap就可能会不断发生扩容,也就使得程序的性能降低了。

    另外,在上面的代码中我们会发现,同样是设置了初始化容量,设置的数值不同也会影响性能,那么当我们已知HashMap中即将存放的KV个数的时候,容量的设置就成了一个问题。

    HashMap中容量的初始化

    开头提到,在默认的情况下,当我们设置HashMap的初始化容量时,实际上HashMap会采用第一个大于该数值的2的幂作为初始化容量。

    Map<String, String> map = new HashMap<>(1);
    map.put("huangq", "yanggb");
    
    Class<?> mapType = map.getClass();
    Method capacity = mapType.getDeclaredMethod("capacity");
    capacity.setAccessible(true);
    System.out.println("capacity : " + capacity.invoke(map)); // 2