手记

比学习新技术更重要的是思维的改变


程序员是一个苦逼的行业,其中重要的一点就是行业专业知识更新的速度非常快,超过其他绝大部分行业,尤其是前台开发,这些天各种框架思想如雨后春笋层出不穷,有时候买了一本书,书还没有学完,技术可能已经更新或者被淘汰。那么,作为程序员的我们,怎么样才能不被爆炸的知识击昏,切实掌握真正属于自己的技术呢?


从我个人的心得来说,学习技术应该重点学习那种新思想新角度的技术,并使用新的思维来写新学习的技术。大部分技术人员的问题不在于前者,而在于学习了新技术,却还是使用老的思维来写新技术,就好比用马拉火车一样,写者落泪看者伤心!


举例,在前台开发中,MVVM框架的出现,相对于jquery天下的前台是一个非常大的改变,我们学习mvvm框架的时候,首先第一步要从思维上认识到这2种技术的不同,要理解数据驱动概念,理解 “jquery是死的,mvvm是活的” 这点,而不是只是把mvvm框架当作一个模板框架来使用。而在实际工作中,出现过有些水平较差的开发人员,还在使用jquery的思维来写vue的框架,结果是vue的好处没有享受到,整个开发部署过程还变得复杂。


后台开发里面,jdk8由于接口默认实现特性,绝对算得上是一个里程碑,其中最重要的函数式编程的思想。lambda表达式和stream流的学习不算复杂,但你学会了api的使用,你就真的掌握了函数式编程吗?我看未必!


我们来看一道题目,计算 集合中 DeptId 和 Type 相同的数据的num总和。需求很常见,看着很简单,各位写写看?


import lombok.Data;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;


/**
*  计算 集合中 DeptId 和 Type 相同的数据的num总和
*/
public class Test {

public static void main(String[] args) {
List<DataBean> totalStocks = new ArrayList<>();

DataBean stock1 = new DataBean();
stock1.setDeptId(2);
stock1.setType(2);
stock1.setNum(2);
totalStocks.add(stock1);

DataBean stock2 = new DataBean();
stock2.setDeptId(2);
stock2.setType(2);
stock2.setNum(3);
totalStocks.add(stock2);

DataBean stock3 = new DataBean();
stock3.setDeptId(3);
stock3.setType(3);
stock3.setNum(5);
totalStocks.add(stock3);

DataBean stock4 = new DataBean();
stock4.setDeptId(3);
stock4.setType(3);
stock4.setNum(4);
totalStocks.add(stock4);

DataBean stock5 = new DataBean();
stock5.setDeptId(4);
stock5.setType(4);
stock5.setNum(10);
totalStocks.add(stock5);

}

}

@Data
class DataBean {

private int type;

private int deptId;

private int num;
}


这条题目是我群里的人问的,他自己用stream写了一个,觉得写的不好,问我应该怎么样写,我当时把这道题目发在群里面,结果发现大部分写得都不太好(建议大家先不要往下看,自己打开ide写写试试)。其中一个常见错误是stream操作中修改了数据,如

//group
Map<String, List<DataBean>> gourps = totalStocks.stream()
       .collect(Collectors.groupingBy(e -> e.getDeptId() + ":" + e.getType()));

// reduce(错误的写法)
List<DataBean> result = gourps.values().stream()
     .reduce(list -> list.stream().reduce((e1, e2) -> {
     e2.setNum(e1.getNum() + e2.getNum());
   return e2;
}).get())
// toList
.collect(Collectors.toList());

System.out.println(result);

还有一种是,自己new了一个ArrayList,然后再流操作之中往list里面加数据的。至于group2次的就不评论了。


我们学习函数式编程里面,函数式强调函数必须是纯函数,不能修改数据,而且是幂等,在stream里面,任何修改数据的行为都是不应该的,修改数据的时候应该返回新对象。所以在里面set数据的,或者往list里面增加数据的,都是不好的写法!我们学习了函数式编程,就应该遵守他的规则和思想。上面的题目,我的写法如下:

List<DataBean> result = totalStocks.stream()
   //group
   .collect(Collectors.groupingBy(e -> e.getDeptId() + ":" + e.getType()))
   // 分组后的list做reduce
   .values().stream().map(list -> list.stream().reduce(Test::combine).get())
   // 收集到list
   .collect(Collectors.toList());

System.out.println(result);


private  static DataBean combine(DataBean e1, DataBean e2){
 DataBean e = new DataBean();

 e.setDeptId(e1.getDeptId());
 e.setType(e1.getType());
 e.setNum(e1.getNum() + e2.getNum());

 return e;
}


我们在看另外一种场景,由于不按照函数式思维编写代码,在不同的jdk版本中,结果不一样,可能导致bug


import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class Demo{
   public  String msg = "初始值";
}

public class LambdaDemo2 {
   public static void main(String[] args) {
       System.out.println("jdk版本:"+ManagementFactory.getRuntimeMXBean().getVmVersion());
       List<Demo> demos  = new ArrayList<Demo>();

       demos.add(new Demo());
       demos.add(new Demo());

       // 这里peek中修改了对象,产生了副作用(side-effects)
       long count = demos.stream().peek(demo -> demo.msg = "peek中修改了").count();
       System.out.println(count);

       // jdk8下下面的demo已经改变
       // jdk10下没有
       demos.stream().forEach(demo -> System.out.println(demo.msg));

   }
}


peek函数,按照函数式编程的规范,是调试用的,是个中间操作,如果你在里面修改了对象,看上去没有问题,但是在JDK8和JDK10中,2种运行结果不一致,JDK10种peek方法直接跳过了。


所以,我们学习一个新技术,可以从下面几个层次了解推进,产生的背景?和之前的技术有什么区别?他的思想(风格)是什么?主要的API和套路。如函数式编程,要了解和命令式编程的区别,一开始可能不太理解,多看几个贴,先有个模糊的概念。然后再看看函数式是怎么样的风格?而不是当作一个新的类库/工作类来使用(很多人是当作工具类使用的想法)。最后系统学习api。当然最重要的还是多写,写完之后看看是不是符合函数式编程的风格,如果看着别扭那就是那个环节出现了问题。


总结,我们学习新技术的同时,除了API的使用之外,最重要的是改变思维,使用新技术的思维来写新的代码,一开始可能有点痛苦,但多写几次之后就会适应,就会感觉到新的技术给我们带来的好处,这是成长的必经之路!


8人推荐
随时随地看视频
慕课网APP

热门评论

老师,这个代码部分显示能不能更改下。。看的也太乱了

老师,这个代码部分显示能不能更改下。。看的也太乱了

Map<Integer, Map<Integer, Integer>> collect = totalStocks.stream().collect(Collectors.groupingBy(DataBean::getDeptId, Collectors.groupingBy(DataBean::getType, Collectors.summingInt(DataBean::getNum))));


查看全部评论