Java Stream Reduce 注意事项
Java Stream Reduce 注意事项
👇出问题的代码,最终得到的List每个对象的属性都完全一样的,甚是邪门
1 |
|
由于代码太过复杂,所以先对逻辑进行剥离,方便观察。 首先先对最复杂的Collectors.reducing()
函数进行简化:
- Collectors被大量引入,所以进行静态import
reducing()
中的mapping
部分是将MatcherResult变为CallboxAppVersion,我们抽象为CallboxAppVersion的constructor
reducing()
中的merge
部分是将两个CallboxAppVersion合并为一个,我们抽象为fun merge()
👇剥离业务逻辑后的代码
1 |
|
观察上面的代码,在return处下了断点,我发现map的key正常,但所有value的属性是一样的,我认为是mapping时出现了问题,随即调整结构,将reduce(new,mapping,merge)
调整为mapping(mapping,reduce(new,merge))
。
1 |
|
保持return处的断点,再次运行后,结果仍然一致,这个时候我发现collect
的values中,所有的value是同一个引用,而代码中有一个new CallboxAppVersion()
作为reducing的初始值,这十分可疑。
但这个new在lambda中,就算使用调试器也不好直接观察,于是到CallboxAppVersion的constructor和merge中下断点。
1 |
|
👇猛然发现在merge方法中return了this,并没有复制实体,于是修改merge实现
1 |
|
修改merge后,返回结果正常。判定是new CallboxAppVersion()
的问题,结合reducing(new CallboxAppVersion(), CallboxAppVersion::new, CallboxAppVersion::merge)
的写法,我们可以反向推断lambda在这里的实现
👇伪代码
1 |
|
lambda用多了老是会觉得对象都是final的不可变也不复用,就感觉这个reduce的设计有点反直觉,如果这个current的变量设计为一个creator函数就不会有问题了,就像下面这样
👇伪代码
1 |
|
对应代码
1 |
|
其他优化
发现CallboxAppVersion类中的merge方法完全可以封装成接口,这个肯定很常用的,而jdk里没带Mergeable接口,Spring带的又没泛型,那就自行封装一个,然后CallboxAppVersion去实现一下。
1 |
|