前言明代王阳明先生在《传习录》谈为学之道时说:
私欲日生,如地上尘,一日不扫,便又有一层 。着实用功,便见道无终穷,愈探愈深,必使精白无一毫不彻方可 。
代码中的"坏味道",如"私欲"如"灰尘",每天都在增加,一日不去清除,便会越累越多 。如果用功去清除这些"坏味道",不仅能提高自己的编码水平,也能使代码变得"精白无一毫不彻" 。这里,整理了日常工作中的一些"坏味道",及清理方法,供大家参考 。
01 如何让代码性能更高?1.1.需要 Map 的主键和取值时,应该迭代 entrySet()
当循环中只需要 Map 的主键时,迭代 keySet() 是正确的 。但是,当需要主键和取值时,迭代 entrySet() 才是更高效的做法,比先迭代 keySet() 后再去 get 取值性能更佳 。
反例:
Map<String, String> map = ...;for (String key : map.keySet()) { String value = https://www.isolves.com/it/cxkf/yy/JAVA/2019-09-29/map.get(key); ...}正例:
Map<String, String> map = ...;for (Map.Entry<String, String> entry : map.entrySet()) { String key = entry.getKey(); String value = https://www.isolves.com/it/cxkf/yy/JAVA/2019-09-29/entry.getValue(); ...}1.2.应该使用 Collection.isEmpty() 检测空
使用 Collection.size() 来检测空逻辑上没有问题,但是使用 Collection.isEmpty() 使得代码更易读,并且可以获得更好的性能 。任何 Collection.isEmpty() 实现的时间复杂度都是 O(1),但是某些 Collection.size() 实现的时间复杂度可能是O(n) 。
反例:
if (collection.size() == 0) { ...}正例:
if (collection.isEmpty()) { ...}如果需要还需要检测 null,可采用 CollectionUtils.isEmpty(collection) 和CollectionUtils.isNotEmpty(collection) 。
1.3.不要把集合对象传给自己
将集合作为参数传递给集合自己的方法要么是一个错误,要么是无意义的代码 。
此外,由于某些方法要求参数在执行期间保持不变,因此将集合传递给自身可能会导致异常行为 。
反例:
List<String> list = new ArrayList<>();list.add("Hello");list.add("World");if (list.containsAll(list)) { // 无意义,总是返回true ...}list.removeAll(list); // 性能差, 直接使用clear()复制代码1.4.集合初始化尽量指定大小
JAVA 的集合类用起来十分方便,但是看源码可知,集合也是有大小限制的 。每次扩容的时间复杂度很有可能是 O(n),所以尽量指定可预知的集合大小,能减少集合的扩容次数 。
反例:
int[] arr = new int[]{1, 2, 3};List<Integer> list = new ArrayList<>();for (int i : arr) { list.add(i);}正例:
int[] arr = new int[]{1, 2, 3};List<Integer> list = new ArrayList<>(arr.length);for (int i : arr) { list.add(i);}1.5.字符串拼接使用 StringBuilder
一般的字符串拼接在编译期 java 会进行优化,但是在循环中字符串拼接,java 编译期无法做到优化,所以需要使用 StringBuilder 进行替换 。
反例:
String s = "";for (int i = 0; i < 10; i++) { s += i;}正例:
String a = "a";String b = "b";String c = "c";String s = a + b + c; // 没问题,java编译器会进行优化StringBuilder sb = new StringBuilder();for (int i = 0; i < 10; i++) { sb.Append(i); // 循环中,java编译器无法进行优化,所以要手动使用StringBuilder}1.6.List的随机访问
大家都知道数组和链表的区别:数组的随机访问效率更高 。当调用方法获取到 List 后,如果想随机访问其中的数据,并不知道该数组内部实现是链表还是数组,怎么办呢?可以判断它是否实现 Randomaccess 接口 。
正例:
// 调用别人的服务获取到listList<Integer> list = otherService.getList();if (list instanceof RandomAccess) { // 内部数组实现,可以随机访问 System.out.println(list.get(list.size() - 1));} else { // 内部可能是链表实现,随机访问效率低}1.7.频繁调用 Collection.contains 方法请使用 Set
在 java 集合类库中,List 的 contains 方法普遍时间复杂度是 O(n),如果在代码中需要频繁调用 contains 方法查找数据,可以先将 list 转换成 HashSet 实现,将 O(n) 的时间复杂度降为 O(1)。
反例:
ArrayList<Integer> list = otherService.getList();for (int i = 0; i <= Integer.MAX_VALUE; i++) { // 时间复杂度O(n) list.contains(i);}正例:
ArrayList<Integer> list = otherService.getList();Set<Integer> set = new HashSet(list);for (int i = 0; i <= Integer.MAX_VALUE; i++) { // 时间复杂度O(1) set.contains(i);}02 如何让代码更优雅?2.1.长整型常量后添加大写 L
推荐阅读
- Java新手如何学习Spring、Struts、Hibernate三大框架?
- 如何快速上手CentOS8
- ps中如何制作影子呢
- 无线宽带路由器除了WiFi上网还能有什么用,如何正确设置DHCP服务
- 「译」 如何做到一秒渲染一个移动页面
- 如何处理人际关系
- 如何去除腋毛
- 如何画舞台妆
- 如何保存乌龙茶
- 什么是轻触开关 轻触开关如何选购