Java边用边学: 我有5种方法写排序
2018-09-01 本文已影响0人
朱和
需求及代码分析
下面的一段简单的排序代码(按照员工的年龄的倒序排列),可以有大概这么5种写法:
final List<Employee> employees = ...;
// 1.匿名类
employees.sort(new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return Integer.compare(e1.getAge(), e2.getAge());
}
}.reversed());
// 2.Lambda
// e2在前,倒序
employees.sort(
(e1, e2) -> Integer.compare(e2.getAge(), e1.getAge())
);
// 3.Named Lambda
final Comparator<Employee> comparator =
(e1, e2) -> Integer.compare(e1.getAge(), e2.getAge());
employees.sort(comparator.reversed());
// 4.复用 + Lambda
employees.sort(
comparingInt((Employee e) -> e.getAge()).reversed()
);
// 5.复用 + 方法引用
employees.sort(
comparingInt(Employee::getAge).reversed()
);
没什么好说的,Java 8之前的经典写法了。(至于是不是需要调用reversed()还是直接把e2放在e1前面,这个后面有答案)
Java 8+的Lambda,由于需要实现倒序,所以程序猿很注意的把e2写在了e1前面了(并配有完美的注释)
- 命名的Lambda
由于Lambda是没有名字的,所以想给它来个倒序,不得不给它先取个名字,然后就可以直接调用reversed()来声明我这个是倒序啊!
- 复用 + Lambda
Java自带的类库其实已经很丰富了,所以直接拿来用吧:Comparator.comparingInt(ToIntFunction)完美符合当前需求,用一段Lambda实现了ToIntFunction。
- 复用 + 方法引用
同上,用方法引用替换了Lambda。
讨论/争论开始
很容易写错的排序
这个排序看上去简单,但其实写错也很容易,大概的错误点包括:
- 都写成e1或e2:
Integer.compare(e1.getAge(), e1.getAge());
- 比较的属性不一致:
Integer.compare(e1.getAge(), e2.getLevel());
- e1和e2的前后位置(涉及到倒序还是顺序)
- 我们的实现3.Named Lambda的排序其实写反了。它的Lambda是倒序(没错啊,我们的业务是需要倒序),问题是它在使用的时候再次反序了下,那就又回到顺序了。
都写成e1/e2 | 比较的属性不一致 | e1和e2的前后位置 | |
---|---|---|---|
#1.匿名类 | 中招 | 中招 | 中招 |
#2.Lambda | 中招 | 中招 | 中招 |
#3.命名的Lambda | 中招 | 中招 | 中招 |
#4.复用+Lambda | |||
#5.复用+方法引用 |
实现#4和#5就很聪明,直接用Java的类库,正确性完全有保证(e1和e2的谁前谁后无需关心)。对于倒序则简单的调用下reversed(),完事!另外#5的方法引用让可读性更进一步。
亲民的函数式编程
从左到右读完这句
employees.sort(
comparingInt(Employee::getAge).reversed()
);
大概可以获得下列信息:
- 对列表进行排序需要:
- 按照员工的年龄排序
- 且是倒序
就这么读过,代码的自说明能力完整的表述了其功能。
然后我们对比下#2.Lambda:
employees.sort(
(e1, e2) -> Integer.compare(e2.getAge(), e1.getAge())
);
其解读大概是这样的:
- 对列表进行排序需要:
- 嗯,e1和e2应该是前后相邻的两个Employee实例
- 都取出了年龄来比较
- e2在前,应该是倒序吧(Code Review的同学比较严谨,查了下Javadoc)。嗯,倒序!
如果需求再复杂点,那我们第5种的优势会更明显。
譬如先按年龄的倒序排列,如果年龄相同,则按性别的顺序排列:
employees.sort(
comparingInt(Employee::getAge).reversed() // 年龄(倒序)
.thenComparing(Employee::getGender) // 第二序列是性别(顺序)
);
换成其他几种写法,估计很难一眼看懂了。
结论
- 方法引用优先于Lambda
- 有用的Java类库,譬如Comparator, Collectors
希望这篇博文能对你有所帮助,喜欢的话点个赞吧!