【Java实战】工作中规范使用Java集合

news2025/1/16 8:30:37

目录

一、前言

二、规范使用Java集合

1.【强制】关于 hashCode 和 equals 的处理,遵循如下规则:

2.【强制】判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式。

3.【强制】在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时,一定要使用参数类型为 BinaryOperator,参数名为 mergeFunction 的方法,否则当出现相同 key 时会抛出IllegalStateException 异常。

4.【强制】在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时,一定要注意当 value为 null 时会抛 NPE 异常。

5.【强制】ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。

6.【强制】使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常。

7.【强制】Collections 类返回的对象,如:emptyList() / singletonList() 等都是 immutable list,不可对其进行添加或者删除元素的操作。

8.【强制】在 subList 场景中,高度注意对父集合元素的增加或删除,均会导致子列表的遍历、增加、删除产生 ConcurrentModificationException 异常。

9.【强制】使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为0 的空数组。

10.【强制】使用 Collection 接口任何实现类的 addAll() 方法时,要对输入的集合参数进行 NPE 判断。

11.【强制】使用工具类 Arrays.asList() 把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/ remove / clear 方法会抛出 UnsupportedOperationException 异常。

12.【强制】泛型通配符来接收返回的数据,此写法的泛型集合不能使用 add 方法,而不能使用 get 方法,两者在接口调用赋值的场景中容易出错。

13.【强制】在无泛型限制定义的集合赋值给泛型限制的集合时,在使用集合元素时,需要进行instanceof 判断,避免抛出 ClassCastException 异常。

14.【强制】不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator 对象加锁。

15.【强制】在 JDK7 版本及以上,Comparator 实现类要满足如下三个条件,不然 Arrays.sort,Collections.sort 会抛 IllegalArgumentException 异常。

16.【推荐】泛型集合使用时,在 JDK7 及以上,使用 diamond 语法或全省略。

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

18.【推荐】使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。

19、【推荐】高度注意 Map 类集合 K / V 能不能存储 null 值的情况,如下表格:

20.【参考】合理利用好集合的有序性(sort)和稳定性(order),避免集合的无序性(unsort)和不稳定性(unorder)带来的负面影响。

21.【参考】利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的contains() 进行遍历去重或者判断包含操作。


一、前言

现代软件行业的高速发展对开发者的综合素质要求越来越高,因为不仅是编程知识点,其它维度的知识点也会影响到软件的最终交付质量。比如:五花八门的错误码会人为地增加排查问题的难度;数据库的表结构和索引设计缺陷带来的系统架构缺陷性能风险;工程结构混乱导致后续项目维护艰难;没有鉴权的漏洞代码容易被黑客攻击等。依据约束力强弱及故障敏感性,规约依次分为【强制】【推荐】【参考】三大类。在延伸的信息中,“说明”对规约做了适当扩展和解释;“正例”提倡什么样的编码和实现方式;“反例”说明需要提防的雷区,以及真实的错误案例。

    现代软件架构的复杂性需要协同开发完成,如何高效地协同呢?无规矩不成方圆,无规范难以协同,比如,制定交通法规表面上是要限制行车权,实际上是保障公众的人身安全,试想如果没有限速,没有红绿灯,谁还敢上路行驶?对软件来说,适当的规范标准绝不是消灭代码内容的创造性、优雅性,而是限制过度个性化,以一种普遍认可的统一方式一起做事,提升协作效率降低沟通成本。代码的字里行间流淌的是软件系统的血液,代码质量的提升是尽可能少踩坑杜绝踩重复的坑,切实提升系统稳定性码出质量

二、规范使用Java集合

1.【强制】关于 hashCode 和 equals 的处理,遵循如下规则:

1)只要覆写 equals,就必须覆写 hashCode。
2)因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆写这两种方法。
3)如果自定义对象作为 Map 的键,那么必须覆写 hashCode 和 equals。
说明:String 因为覆写了 hashCode 和 equals 方法,所以可以愉快地将 String 对象作为 key 来使用。


2.【强制】判断所有集合内部的元素是否为空,使用 isEmpty() 方法,而不是 size() == 0 的方式。

说明:在某些集合中,前者的时间复杂度为 O(1),而且可读性更好。
正例:
 

Map<String, Object> map = new HashMap<>(16);
if (map.isEmpty()) {
System.out.println("no element in this map.");
}


3.【强制】在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时,一定要使用参数类型为 BinaryOperator,参数名为 mergeFunction 的方法,否则当出现相同 key 时会抛出
IllegalStateException 异常。


说明:参数 mergeFunction 的作用是当出现 key 重复时,自定义对 value 的处理策略。
正例:

List<Pair<String, Double>> pairArrayList = new ArrayList<>(3);
pairArrayList.add(new Pair<>("version", 12.10));
pairArrayList.add(new Pair<>("version", 12.19));
pairArrayList.add(new Pair<>("version", 6.28));
// 生成的 map 集合中只有一个键值对:{version=6.28}
Map<String, Double> map = pairArrayList.stream()
.collect(Collectors.toMap(Pair::getKey, Pair::getValue, (v1, v2) -> v2));


反例:

String[] departments = new String[]{"RDC", "RDC", "KKB"};
// 抛出 IllegalStateException 异常
Map<Integer, String> map = Arrays.stream(departments)
.collect(Collectors.toMap(String::hashCode, str -> str));


4.【强制】在使用 java.util.stream.Collectors 类的 toMap() 方法转为 Map 集合时,一定要注意当 value为 null 时会抛 NPE 异常。


说明:在 java.util.HashMap 的 merge 方法里会进行如下的判断:

if (value == null || remappingFunction == null)
throw new NullPointerException();


反例:

List<Pair<String, Double>> pairArrayList = new ArrayList<>(2);
pairArrayList.add(new Pair<>("version1", 8.3));
pairArrayList.add(new Pair<>("version2", null));
// 抛出 NullPointerException 异常
Map<String, Double> map = pairArrayList.stream()
.collect(Collectors.toMap(Pair::getKey, Pair::getValue, (v1, v2) -> v2));


5.【强制】ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。


说明:subList() 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList 本身,而是 ArrayList 的一个视图,对于SubList 的所有操作最终会反映到原列表上。同时注意数组越界异常


6.【强制】使用 Map 的方法 keySet() / values() / entrySet() 返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常。


7.【强制】Collections 类返回的对象,如:emptyList() / singletonList() 等都是 immutable list,不可对其进行添加或者删除元素的操作。


反例:如果查询无结果,返回 Collections.emptyList() 空集合对象,调用方一旦在返回的集合中进行了添加元素的操作,就会触发 UnsupportedOperationException 异常。


8.【强制】在 subList 场景中,高度注意对父集合元素的增加或删除,均会导致子列表的遍历、增加、删除产生 ConcurrentModificationException 异常。


说明:抽查表明,90% 的程序员对此知识点都有错误的认知。


9.【强制】使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为0 的空数组。


反例:直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现ClassCastException 错误。
正例:

List<String> list = new ArrayList<>(2);
list.add("guan");
list.add("bao");
String[] array = list.toArray(new String[0]);


说明:使用 toArray 带参方法,数组空间大小的 length:
1)等于 0,动态创建与 size 相同的数组,性能最好。
2)大于 0 但小于 size,重新创建大小等于 size 的数组,增加 GC 负担。
3)等于 size,在高并发情况下,数组创建完成之后,size 正在变大的情况下,负面影响与 2 相同。
4)大于 size,空间浪费,且在 size 处插入 null 值,存在 NPE 隐患。


10.【强制】使用 Collection 接口任何实现类的 addAll() 方法时,要对输入的集合参数进行 NPE 判断。


说明:在 ArrayList#addAll 方法的第一行代码即 Object[] a = c.toArray();其中 c 为输入集合参数,如果为 null,则直接抛出异常。


11.【强制】使用工具类 Arrays.asList() 把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/ remove / clear 方法会抛出 UnsupportedOperationException 异常。


说明:asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组。
String[] str = new String[]{ "yang", "guan", "bao" };
List list = Arrays.asList(str);
第一种情况:list.add("yangguanbao"); 运行时异常。
第二种情况:str[0] = "change"; list 中的元素也会随之修改,反之亦然。


12.【强制】泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方法,而<? super T>不能使用 get 方法,两者在接口调用赋值的场景中容易出错。


说明:扩展说一下 PECS(Producer Extends Consumer Super) 原则,即频繁往外读取内容的,适合用<? extends T>,经常往里插入的,适合用<? super T>


13.【强制】在无泛型限制定义的集合赋值给泛型限制的集合时,在使用集合元素时,需要进行
instanceof 判断,避免抛出 ClassCastException 异常。


说明:毕竟泛型是在 JDK5 后才出现,考虑到向前兼容,编译器是允许非泛型集合与泛型集合互相赋值。


反例:

List<String> generics = null;
List notGenerics = new ArrayList(10);
notGenerics.add(new Object());
notGenerics.add(new Integer(1));
generics = notGenerics;
// 此处抛出 ClassCastException 异常
String string = generics.get(0);


14.【强制】不要在 foreach 循环里进行元素的 remove / add 操作。remove 元素请使用 iterator 方式,如果并发操作,需要对 iterator 对象加锁。


正例:

List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (删除元素的条件) {
iterator.remove();
}
}


反例:

for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}


说明:反例中的执行结果肯定会出乎大家的意料,那么试一下把“1”换成“2”会是同样的结果吗?


15.【强制】在 JDK7 版本及以上,Comparator 实现类要满足如下三个条件,不然 Arrays.sort,Collections.sort 会抛 IllegalArgumentException 异常。


说明:三个条件如下
1)x,y 的比较结果和 y,x 的比较结果相反。
2)x > y,y > z,则 x > z。
3)x = y,则 x,z 比较结果和 y,z 比较结果相同。


反例:下例中没有处理相等的情况,交换两个对象判断结果并不互反,不符合第一个条件,在实际使用中可能会出现异常。

new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getId() > o2.getId() ? 1 : -1;
}
};


16.【推荐】泛型集合使用时,在 JDK7 及以上,使用 diamond 语法或全省略。


说明:菱形泛型,即 diamond,直接使用<>来指代前边已经指定的类型。
正例:
 

// diamond 方式,即<>
HashMap<String, String> userCache = new HashMap<>(16);
// 全省略方式
ArrayList<User> users = new ArrayList(10);


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


说明:HashMap 使用构造方法 HashMap(int initialCapacity) 进行初始化时,如果暂时无法确定集合大小,那么指定默认值(16)即可。
正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loaderfactor)默认为 0.75,如果暂时无法确定初始值大小,请设置为 16(即默认值)。
反例:HashMap 需要放置 1024 个元素,由于没有设置容量初始大小,随着元素增加而被迫不断扩容,resize() 方法总共会调用 8 次,反复重建哈希表和数据迁移。当放置的集合元素个数达千万级时会影响程序性能。


18.【推荐】使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。


说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的 value。而entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。如果是 JDK8,使用 Map.forEach 方法。
正例:values() 返回的是 V 值集合,是一个 list 集合对象;keySet() 返回的是 K 值集合,是一个 Set 集合对象;entrySet() 返回的是 K-V 值组合的 Set 集合。

19、【推荐】高度注意 Map 类集合 K / V 能不能存储 null 值的情况,如下表格:

集合类
Key
Value
Super
说明
Hashtable
不允许为 null
不允许为 null
Dictionary
线程安全
TreeMap
不允许为 null
允许为 null
AbstractMap
线程不安全
ConcurrentHashMap
不允许为 null
不允许为 null
AbstractMap
锁分段技术(JDK8:CAS)
HashMap
允许为 null
允许为 null
AbstractMap
线程不安全
反例: 由于 HashMap 的干扰,很多人认为 ConcurrentHashMap 是可以置入 null 值,而事实上,存储 null 值时会抛 出 NPE 异常。

20.【参考】合理利用好集合的有序性(sort)和稳定性(order),避免集合的无序性(unsort)和不稳定性(unorder)带来的负面影响。

说明:有序性是指遍历的结果是按某种比较规则依次排列的,稳定性指集合每次遍历的元素次序是一定的。如:ArrayList 是 order / unsort;HashMap 是 unorder / unsort;TreeSet 是 order / sort。


21.【参考】利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的contains() 进行遍历去重或者判断包含操作。

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

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

相关文章

接口自动化测试实践指导(中):接口测试场景有哪些

在第一篇文章中详细给小伙伴们讲解了接口自动化需要做哪些准备工作&#xff0c;准备工作中最后一步接口测试用例设计是非常重要的一个环节&#xff0c;用例设计的好不好&#xff0c;直接关系到我们的测试质量。那如何进行测试用例设计呢&#xff1f;这里呢我结合自身经验&#…

PYTHON 用几何布朗运动模型和蒙特卡罗MONTE CARLO随机过程模拟股票价格可视化分析耐克NKE股价时间序列数据...

原文链接&#xff1a;http://tecdat.cn/?p27099 金融资产/证券已使用多种技术进行建模。该项目的主要目标是使用几何布朗运动模型和蒙特卡罗模拟来模拟股票价格。该模型基于受乘性噪声影响的随机&#xff08;与确定性相反&#xff09;变量&#xff08;点击文末“阅读原文”获取…

【 医学影像| 数据预处理】

影像读取及预处理&#xff1a;预处理后的数据集建议保存在本地&#xff0c;可以减少训练时的部分资源消耗。里面提到了归一化的 对分割的一些理解&#xff1a;基于深度学习来做医学图像处理&#xff0c;主要的工作集中在了数据预处理部分&#xff1a;深入理解医学图像的格式和特…

GLAD:体全息

概述 自从伽伯1948年提出全息术后&#xff0c;光学全息术已经被广泛用于三维光学成像领域。体全息成像技术是采用体全息光栅作为成像元件对物体进行三维成像的技术。 1990年,由Barbastathis和Brady提出体全息成像技术&#xff0c;采用体全息光栅作为选择成像元件&#xf…

【微信小程序高频面试题——精选一】

微信小程序高频面试题小程序中如何进行接口请求&#xff1f;会不会跨域&#xff0c;为什么小程序的常用命令有哪些你认为微信小程序的优点是什么&#xff0c;缺点是什么微信小程序中的js和浏览器中的js以及node中的js的区别微信小程序中的数据渲染浏览器中有什么不同小程序中如…

全国所有地级市环境污染、企业、公路、固定资产、外商投资-最新面板数据

一、1990&#xff0d;2019年地级市面板数据 1、数据来源&#xff1a;中国城市统计年鉴、WIND数据库 2、时间跨度&#xff1a;2000-2019 3、区域范围&#xff1a;所有地级市 4、指标说明&#xff1a; 该份部分数据指标如下&#xff1a; 主营业务税金及附加(万元) 发明专利…

android-CHECK_xxx分析

android-CHECK_xxx 在android源码中有不少类似这样的用法&#xff0c;上图中就是检查获得的hal版本是否大于等于版本1_3&#xff0c;满足继续往下走&#xff0c;不满足则assert&#xff0c;并报错。 接下来就展开看看CHECK_xx家族&#xff1a; 用法 类型用法含义CHECK_EQ(val…

【SpringCloud】07 流量管理sentinel

sentinel Sentinel 是面向分布式服务架构的高可用流量防护组件&#xff0c;主要以流量为切入点&#xff0c;从限流、流量整形、熔断降级、系统负载保护、热点防护等多个维度来帮助开发者保障微服务的稳定性。 1. 微服务中的服务雪崩 服务雪崩效应是一种因“服务提供者的不可…

Springboot系列(二十二):如何纯文本转成.csv格式文件?|超级详细,建议收藏

一、前言&#x1f525; 不知道大家有咩有遇到这么个需求&#xff0c;给你一长串文本&#xff0c;要求你能导成excel格式展示数据&#xff0c;一时间我陷入了沉思&#xff0c;如果要常规转excel&#xff0c;最明显的一点就是固定表头名&#xff0c;然而并不是&#xff0c;这表头…

[附源码]计算机毕业设计springboot冬奥资讯系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【DL with Pytorch】第 2 章 : 神经网络的构建块

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

第一个Shader Graph

上篇我们用ShaderLab来实现了第一个Shader,但对于初学者也太复杂了,那有没有简单的方式来实现shader的操作呢? 现在我们来分享下ShaderGraph,可视化编程,如图所示 ShaderGraph介绍 ShaderGraph是2018年推出的,可以看下官网出的例子https://github.com/UnityTechnologi…

[Linux] 进程程序替换之实现一个简单的shell

进程程序替换替换原理替换函数实现一个简单的shell主要过程实现代码替换原理 用fork创建子进程后执行的是和父进程相同的程序&#xff0c;若要执行不同的代码分支&#xff0c;子进程往往要调用一种exec函数以执行另一个程序&#xff1b;当进程调用一种exec函数时&#xff0c;该…

信息论与编码:随参信道特性

文章目录随参信道数学模型的建立随参信道对信号传输的影响平坦性衰落及频率选择性衰落1.平坦性衰落Rayleigh 分布Rice 分布2.频率选择性衰落多径随参信道的时延扩展与相干带宽随参信道的多径时延特性多径信道的频域特性移动信道的多普勒扩展及相干时间1.多普勒扩展2.信道的相干…

nodejs大前端从入门到精通一

一、nodejs架构 nodejs核心组成Natives Modules 当前层内容由JS实现提供应用程序可直接调用库&#xff0c;例如fs、path、http等JS语文无法直接操作底层硬件设置 在和硬件交互的的桥梁&#xff0c;通过Builtin Modules(胶水层) 底层&#xff1a; V8&#xff1a;执行JS代码&…

Android——Theme和Style-由浅入深,全面讲解

1、官方详细解读 样式和主题背景 | Android 开发者 | Android Developers 2、应用场景 类似web设计中css样式。将应用设计的细节与界面的结构和行为分开。 样式style &#xff1a;应用于 单个 View 的外观。样式可以指定字体颜色、字号、背景颜色等属性 主题theme&…

【仿牛客网笔记】项目发布与总结——单元测试、项目监控

在项目上线之前需做好单元测试&#xff0c;平时开发的过程中&#xff0c;每个功能也需要进行单元测试。 验证注解的作用&#xff0c;注解是修饰方法的。 每次调方法都是静态的 对test1和test2分别进行运行 通过类进行运行&#xff0c;运行所有的方法 测试帖子的Service&#x…

【序列召回推荐】(task5)多兴趣召回Comirec-DR

note&#xff1a; 多兴趣召回建模。Comirec论文中的提出的第一个模型&#xff1a;Comirec-DR&#xff08;DR就是dynamic routing&#xff09;&#xff0c;阿里将用户行为序列的item embeddings作为初始的capsule&#xff0c;然后提取出多个兴趣capsules&#xff0c;即为用户的…

【Java】博客系统——详细解释+代码+详细注释(课设必过)

目录 前言 博客系统简要分析 一、数据库的设计 1.1 分析 1.2 代码实现&#xff08;创建数据库和表&#xff09; 二、封装数据库&#xff08;JDBC代码的编写&#xff09; 2.1、首先通过创建Maven项目&#xff0c;基于Small Tomcat部署 servlet&#xff1b; 2.2、封装数据…

telnet配置设备远程管理—eNSP

案例&#xff1a;给路由器配置远程管理&#xff0c;使一台路由器远程管理另一台。 所需设备&#xff1a;两台路由器&#xff0c;一根网线 图示 一、给两台设备配置IP地址 AR1&#xff08;以下命令&#xff09; a. sy b. int g0/0/0 c. ip add 1.1.1.1 24AR2 a. sy b. int g0/0…