这是个人在写代码时的一个优化心得,可以让代码更清晰,更具可维护性
有时候我们会看到类似这样的代码:
- if (a = b) {
- if (c = d) {
- if (e = f) {
- // 代码执行逻辑
- .....
- }
- }
- }
这种代码嵌套越多越是难以维护,在某些情况下我们可以更改成这样:
- if (a != b) {
- return;
- }
- if (c != d) {
- return;
- }
- if (e != f) {
- return;
- }
-
- // 代码执行逻辑
- ...........
这样的代码就非常清晰了,但是在某些情况下我们不能直接return还需要将代码继续执行下去,例如:
- private void funName() {
- // 当这里执行完毕后
- if (a = b) {
- if (c = d) {
- if (e = f) {
- // 代码执行逻辑
- .....
- }
- }
- }
- // 继续往下执行
- // 第二层代码执行逻辑
- .........
- }
此时我们可以封装一层方法将来处理上面那层代码逻辑例如:
- private void funName() {
- InvokedFun()
- // 继续执行代码逻辑
- .........
- }
-
- private void InvokedFun() {
-
- if (a != b) {
- return;
- }
- if (c != d) {
- return;
- }
- if (e != f) {
- return;
- }
-
- // 代码执行逻辑
- ...........
- }
基本以上两种技巧如果应用得当代码会清晰很多,一定程度商可以帮自己理清思路,并降低bug 出现的几率。
2022年11月25日 补充
在写代码的过程中可能会出现这样一种情况:
- private void funName(Map
entityMap,Integer id) { -
- if (entityMap != null) {
- Entity entity = entityMap.get(id)
- if (entity != null) {
- // 执行代码逻辑A
- .......
- }
- } else {
- Entity entity = new Entity()
- // 设置相关属性
- .........
- // 执行代码逻辑A
- .........
- entityMap.put(id,entity)
- }
- }
上面的代码逻辑A 执行的是完全相同的代码,代码流程问题不得不写两次或者重新写一个方法进行封装,可以改成下面这种方式:
- private void funName(Map
entityMap,Integer id) { - Entity entity = entityMap.get(id)
- if (entityMap == null) {
- Entity entity = new Entity()
- // 设置相关属性
- .........
- entityMap.put(id,entity)
- }
-
- // 执行代码逻辑A
- .........
- }
因为代码逻辑A 在为空的情况下和不为空的情况下都必须执行,而这里的if else 只是为了保证数据不为空,这时候这样处理代码会清晰简洁很多
同样的道理,for 循环也会产生相同的问题,不断嵌套导致代码不清晰,例如:
- List
demo = new ArrayList<>; - for(int i = 0;i < demo.size(); i++) {
- // 从数据库或其他地方获取数据
- List
entityItemList = entityMapper.listByParentId(demo.getId()); - for (int n = 0; n < entityList.size(); i++){
- EntityItem entityItem = entityItemList.get(i);
- // 代码执行逻辑
- ...........
- }
- }
两层代码嵌套看起来没有那么乱,但是如果从第三层嵌套开始代码清晰度就会直线下降,这时候可以这样写:
- List
demo = new ArrayList<>; - List
entityItemListAll = new ArrayList(); - for(int i = 0;i < demo.size(); i++) {
- // 从数据库或其他地方获取数据
- List
entityItemList = entityMapper.listByParentId(demo.getId()); - entityItemListAll.addAll(entityItemList);
- }
-
-
- for (int n = 0; n < entityList.size(); i++){
- EntityItem entityItem = entityItemList.get(i);
- // 代码执行逻辑
- ...........
- }
算是以空间换代码整洁度,但是自己想想,这里真正占用到内存的只有entityItemListAll 的创建,因为即使用entityItemListAll.addAll(entityItemList);将数据添加到新的list 当中,其实只是将原list 的每个数据的内存地址进行了拷贝,并不会新建对象,为此我做了以下测试:
- List
listA = new ArrayList<>(); - listA.add("1");
- listA.add("2");
- listA.add("3");
- List
listB = new ArrayList<>(); - listB.addAll(listA);
- for (int i = 0; i < listA.size(); i++) {
- String item = listA.get(i);
- System.out.println(System.identityHashCode(item));
- }
- for (int i = 0; i < listB.size(); i++) {
- String item = listB.get(i);
- System.out.println(System.identityHashCode(item));
- }
我使用了以下代码对内存地址进行了打印:
打印内存地址参考:java打印对象内存地址 | kennedyhan
System.identityHashCode(item)
最终发现listB中的内存地址并没有发生改变:
- Connected to the target VM, address: '127.0.0.1:62988', transport: 'socket'
- 93122545
- 2083562754
- 1239731077
- 93122545
- 2083562754
- 1239731077
- Disconnected from the target VM, address: '127.0.0.1:62988', transport: 'socket'
-
- Process finished with exit code 0
所以这里并没有占用新的对象,而创建一个List 个人认为成本可以忽略不计,因为方法一旦完结则方法内的所有对象将会被gc 回收。
2022年12月20日 补充
还有一种情况如下所示:
- for (Entity entity: entityList) {
- for (SearchEntity search: searchEntitylist) {
- if (entity.seatchId == search.id) {
- // 执行任务流程
- ......
- }
- }
- }
可以看出,每次entity 循环都会遍历一次SearchEntity这是没必要的,可以通过以下方式降低循环次数,并提高代码清晰度:
- Map
searchEntityMap = new HashMap(); - for (SearchEntity search: searchEntitylist) {
- searchEntityMap.put(search.id,search);
- }
-
- for (Entity entity: entityList) {
- SearchEntity search = searchEntityMap.get(entity.searchId);
- if (search == null) {
- continue;
- }
- // 执行业务逻辑
- .......
- }