码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 工作中规范使用Java集合


    目录

    一、前言

    二、规范使用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),而且可读性更好。
    正例:
     

    1. Map map = new HashMap<>(16);
    2. if (map.isEmpty()) {
    3. System.out.println("no element in this map.");
    4. }


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


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

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


    反例:

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


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


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

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


    反例:

    1. List> pairArrayList = new ArrayList<>(2);
    2. pairArrayList.add(new Pair<>("version1", 8.3));
    3. pairArrayList.add(new Pair<>("version2", null));
    4. // 抛出 NullPointerException 异常
    5. Map map = pairArrayList.stream()
    6. .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 错误。
    正例:

    1. List list = new ArrayList<>(2);
    2. list.add("guan");
    3. list.add("bao");
    4. 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.【强制】泛型通配符来接收返回的数据,此写法的泛型集合不能使用 add 方法,而不能使用 get 方法,两者在接口调用赋值的场景中容易出错。


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


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


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


    反例:

    1. List generics = null;
    2. List notGenerics = new ArrayList(10);
    3. notGenerics.add(new Object());
    4. notGenerics.add(new Integer(1));
    5. generics = notGenerics;
    6. // 此处抛出 ClassCastException 异常
    7. String string = generics.get(0);


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


    正例:

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


    反例:

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


    说明:反例中的执行结果肯定会出乎大家的意料,那么试一下把“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 比较结果相同。


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

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


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


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

    1. // diamond 方式,即<>
    2. HashMap userCache = new HashMap<>(16);
    3. // 全省略方式
    4. ArrayList 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() 进行遍历去重或者判断包含操作。

  • 相关阅读:
    vue watch 侦听器 监视器
    paddle Conv2D参数,在手撕数字识别案例中调参数;卷积神经网络的卷积核大小、个数,卷积层数如何确定呢?
    【OpenCV】Chapter5.空间域图像滤波
    暗流涌动的智能家居,和顺势已飞的三翼鸟
    智创未来 · 引领5G价值,广和通携5G AIoT创新应用亮相2022德国慕尼黑电子展
    关于 虚拟 DOM、Shadow DOM 和 DocumentFragment
    链表经典面试题(二)
    Sympy-nonlinsolve-只保留实数解问题
    精密云工程:智能激活业务速率 ——华为云11.11联合大促倒计时 仅剩3日
    为什么选择clickhouse?
  • 原文地址:https://blog.csdn.net/qq_57756904/article/details/128065089
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号