阿里巴巴为什么让初始化集合时必须指定大小?

这是我的第 205 期分享

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

哈喽,亲爱的小伙伴们,技术学磊哥,进步没得说!欢迎来到新一期的性能解读系列,我是磊哥。

今天给大家带来的是关于阿里巴巴《Java开发手册》泰山版(最新)中关于集合初始化时的性能建议

阿里巴巴《Java开发手册》第 1 章编程规范,第 6 节集合处理的第 17 条规定如下:

【推荐】集合初始化时,指定集合初始值大小。

说明:HashMap 使用 HashMap(int initialCapacity) 初始化,如果暂时无法确定集合大小,那么指定默认值(16)即可。

正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loader factor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。

反例:HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素不断增加,容量 7 次被迫扩大,resize 需要重建 hash 表。当放置的集合元素个数达千万级别时,不断扩容会严重影响性能。

规范解读

此规范的主要目的完全是出于性能考虑,查看 HashMap 的源码也就可以发现此规范的原因,如果我们能为集合设置合理的大小就可以避免 HashMap 的扩容操作,而 HashMap 的扩容方法 resize 有很多逻辑判断和业务操作,如果设置了合理的大小就可以避免执行更多的代码,因此就可以更大限度的提高集合的执行效率,HashMap 的 resize 源码如下:

// 源码基于 JDK 8
final Node<K,V>[] resize() {// 扩容前的数组Node<K,V>[] oldTab = table;// 扩容前的数组的大小和阈值int oldCap = (oldTab == null) ? 0 : oldTab.length;int oldThr = threshold;// 预定义新数组的大小和阈值int newCap, newThr = 0;if (oldCap > 0) {// 超过最大值就不再扩容了if (oldCap >= MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return oldTab;}// 扩大容量为当前容量的两倍,但不能超过 MAXIMUM_CAPACITYelse if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)newThr = oldThr << 1; // double threshold}// 当前数组没有数据,使用初始化的值else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;else {               // zero initial threshold signifies using defaults// 如果初始化的值为 0,则使用默认的初始化容量newCap = DEFAULT_INITIAL_CAPACITY;newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);}// 如果新的容量等于 0if (newThr == 0) {float ft = (float)newCap * loadFactor;newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"})Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];// 开始扩容,将新的容量赋值给 tabletable = newTab;// 原数据不为空,将原数据复制到新 table 中if (oldTab != null) {// 根据容量循环数组,复制非空元素到新 tablefor (int j = 0; j < oldCap; ++j) {Node<K,V> e;if ((e = oldTab[j]) != null) {oldTab[j] = null;// 如果链表只有一个,则进行直接赋值if (e.next == null)newTab[e.hash & (newCap - 1)] = e;else if (e instanceof TreeNode)// 红黑树相关的操作((TreeNode<K,V>)e).split(this, newTab, j, oldCap);else { // preserve order// 链表复制,JDK 1.8 扩容优化部分Node<K,V> loHead = null, loTail = null;Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;do {next = e.next;// 原索引if ((e.hash & oldCap) == 0) {if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}// 原索引 + oldCapelse {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}} while ((e = next) != null);// 将原索引放到哈希桶中if (loTail != null) {loTail.next = null;newTab[j] = loHead;}// 将原索引 + oldCap 放到哈希桶中if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;
}

性能评测

接下来我们来测试一下设置 size 的性能和不设置 size 的性能差别,我们已知需要插入 1024 个数据,根据默认的负载因子 0.75 和公式 (存储元素个数/负载因子)+1 得出需要设置的大小为 1367(取整)。

小贴士:公式“(存储元素个数/负载因子)+1”说明:因为 HashMap 的实际存储量等于:元素个数*负载因子,为了防止 HashMap 扩容,所以公式必须是“(存储元素个数/负载因子)+1”才能防止动态扩容。

本文我们依旧使用 Oracle 官方提供的 JMH(Java Microbenchmark Harness,JAVA 微基准测试套件)测试框架,首先现在 pom.xml 中添加 JMH 引用,配置如下:

<!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core -->
<dependency><groupId>org.openjdk.jmh</groupId><artifactId>jmh-core</artifactId><version>{version}</version>
</dependency>

然后编写完整的测试代码:

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;@BenchmarkMode(Mode.AverageTime) // 测试完成时间
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2, time = 1, timeUnit = TimeUnit.SECONDS) // 预热 2 轮,每次 1s
@Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS) // 测试 5 轮,每次 3s
@Fork(1) // fork 1 个线程
@State(Scope.Thread) // 每个测试线程一个实例
public class AlibabaHashMapTest {public static void main(String[] args) throws RunnerException {// 启动基准测试Options opt = new OptionsBuilder().include(AlibabaHashMapTest.class.getSimpleName()) // 要导入的测试类.build();new Runner(opt).run(); // 执行测试}@Benchmarkpublic void noSizeTest(Blackhole blackhole) {Map map = new HashMap();for (int i = 0; i < 1024; i++) {map.put(i, i);}// 为了避免 JIT 忽略未被使用的结果blackhole.consume(map);}@Benchmarkpublic void setSizeTest(Blackhole blackhole) {Map map = new HashMap(1367);for (int i = 0; i < 1024; i++) {map.put(i, i);}// 为了避免 JIT 忽略未被使用的结果blackhole.consume(map);}
}

测试结果如下:


从上述结果可以看出,设置了大小的 HashMap 的性能约是没有设置大小的 1.29 倍。

总结

在初始化集合时,如果已知集合的数量,那么一定要在初始化时设置集合的容量大小,这样就可以有效的提高集合的性能,但需要注意的是 HashMap 的实际存储量是“元素个数*负载因子”,而负载因子默认是 0.75,因此在设置大小时,要使用“(存储元素个数/负载因子)+1”的公式计算出正确的值再进行设置。

往期推荐

阿里新版《Java 开发手册(泰山版)》内容解读(附下载地址)

阿里为什么禁用Executors创建线程池?

关注公众号发送”进群“,磊哥拉你进读者群。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/546102.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ios页面间跳转方式总结

转自&#xff1a;http://www.cnblogs.com/anywherego/p/3542202.html 下面以OldViewController(oldC)的按钮btn点击后跳转到NewViewController(newC)为例说明: 1.Storyboard的segues方式 鼠标点击按钮btn然后按住control键拖拽到newC页面&#xff0c;在弹出的segue页面中选择跳…

__asm___错误:“”前应有'=',',',',','asm'或'_attribute_'

__asm__A very common error in C programming language, it occurs when # is not used before the include. 这是C编程语言中非常常见的错误&#xff0c;当在include之前不使用&#xff03;时&#xff0c;就会发生此错误。 As we know that #include is a preprocessor dire…

Photoshop CS3 ICO 图标保存插件

最近编程发现&#xff0c;没有啥好看的图标文件于是&#xff0c;本人使用功能强大的ps&#xff0c;制作了图标文件做后发现&#xff0c;无法保存为ico图标文件在网上搜索了半天&#xff0c;终于从茫茫网海找到ico保存插件下载存放的地方是 PS根目录 即Adobe\Adobe Photoshop CS…

zoj 1005 jugs

题目内容见zoj1005 由于A&#xff0c;B互素且A的容量小于B&#xff0c;那么可以将B装满并且倒入A中&#xff0c;如果A被装满则将A中的内容全部清空&#xff0c;一直进行下去直到某一刻B中容量恰好等于目标的容量。这种方法能得到正确的结果&#xff0c;但是通常得不到最优结果…

啪啪打脸!领导说:try-catch要放在循环体外!

这是我的第 206 期分享作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;哈喽&#xff0c;亲爱的小伙伴们&#xff0c;技术学磊哥&#xff0c;进步没得说&#xff01;欢迎来到…

人类智商一般在多少左右?爱因斯坦的智商是多少?

智商 智商就是智力商数。智力通常叫智慧&#xff0c;也叫智能。是人们认识客观事物并运用知识解决实际问题的能力。智力包含多个方面&#xff0c;如观察力、记忆力、想象力、分析推断能力、思维能力、应变能力等。智力的高低通经常使用智力商数来表示&#xff0c;是用以标示智力…

软件项目与产品的区别与联系_软件产品和软件过程之间的区别和关系

软件项目与产品的区别与联系软件产品和软件过程 (Software product and Software process) Software product and Software process: These two words are the one which is mostly confused with each other. In this article, we are going to explain each of these in deta…

Oracle官方推荐的性能测试工具!简单、精准又直观!

这是我的第 207 期分享作者 | 武培轩来源 | 武培轩&#xff08;ID&#xff1a;wupeixuan404&#xff09;分享 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;磊哥在前面的所有性能优化的文章中都是用了这款性能测试工具——JMH&#xff0c;一直没来得及给大家…

系统美化 XP主题及其他

赢狗系统主题&#xff1a;下载马头桌名主题&#xff1a;下载windows美化大师&#xff1a;下载虽然电脑系统层出不穷但是仍对win xp有爱

zoj 3488 conic section

题目见zoj 3488 很简单的题目&#xff0c;却没能一次搞定&#xff0c;因为没看清楚题目中输入数据都是实数。 该题目考察浮点数的比较&#xff08;因为浮点数在计算机中存储是近似存储&#xff0c;所以不能直接将两个浮点数直接用大于或小于符号相比较&#xff09; /* zoj 3…

Hadoop开发第2期---虚拟机中搭建Linux

注&#xff1a;关于如何将hadoop源码导入Eclipse详见http://pan.baidu.com/s/1hq8ArUs 一、Hadoop配置软件&#xff08;我的电脑是Windows7旗舰--64bit&#xff09; 1. VMWare专用CentOS镜像(Centos是Linux操作系统的一种)2. VMware-workstation103. hadoop-1.1.2.tar.gz4. jdk…

c构造函数和析构函数_C ++构造函数,析构函数能力问题和答案(第2组)

c构造函数和析构函数C 构造函数和析构函数能力问题列表 (List of C Constructor and Destructor Aptitude Questions & Answers) 1) Constructor(s) which is/are added automatically with a class, if we do not create our own constructor? 1)如果我们不创建自己的构造…

看故事学知识,这篇Java代理的文章妙啊!

这是我的第 208 期分享作者 | java金融来源 | java金融&#xff08;ID&#xff1a;java4299&#xff09;分享 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;什么是代理代理模式是常用的java设计模式&#xff0c;他的特征是代理类与委托类有同样的接口&#x…

[下载]青岛交通旅游地图[download]

清闲无事 所以就把青岛地图扫描了一下有需要青岛旅游地图的可以下载the download 【下载】

Java Thread类的静态void sleep(long time_in_ms)方法,带示例

线程类静态无效睡眠(long time_in_ms) (Thread Class static void sleep(long time_in_ms)) This method is available in package java.lang.Thread.sleep(long time_in_ms). 软件包java.lang.Thread.sleep(long time_in_ms)中提供了此方法。 sleep(long time_in_ms) method i…

阿里《Java开发手册》中的 1 个bug!

这是我的第 210 期分享作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;本来打算写一篇《阿里巴巴为什么不允许日志输出时&#xff0c;使用字符串拼接&#xff1f;》的文章&a…

zoj 1006 do the untwist

题目见zoj 1006 或poj 1317 简单的解密算法&#xff0c;直接套用题目中公式即可。 /* zoj 1006 Do the Untwist */ #include <stdio.h> #include <string.h>#define MAXLEN 80 #define MAGICNUM 28char num2Char(int n); int char2Num(char c); int main(void)…

安装完SqlServer2008,wamp服务器无法启动的问题

"开始"->"程序"->Microsoft SQL Server 2008->配置工具->SQL Server配置管理器->SQL Server服务: 只保留SQL Server(MSSQLSERVER)(正在运行)&#xff0c;其他的全部设为停止。 重启wamp服务器成功!

猫版超级玛丽 附下载

不再多言 玩者自知しょぼんのアクション猫版超级玛丽 下载

驳《阿里「Java开发手册」中的1个bug》?

这是我的第 211 期分享作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;前两天写了一篇关于《阿里Java开发手册中的 1 个bug》的文章&#xff0c;评论区有点炸锅了&#xff0…