stream and lambda(10) - 中间操作之排序sorted与调试peek
Stream 操作过程中,有可能会对元素进行排序,这点 Stream 支持的也不输集合。考虑到流内操作只有在终止操作时才会触发导致不便调试,Stream 也提供了偷窃利器 peek 来方便我们调试。
sorted
方法定义
Stream<T> sorted(); Stream<T> sorted(Comparator<? super T> comparator);
sorted
提供了两个方法,一个无参,一个需要传入比较器。两种方法运行过后,都可以将流内的元素排序。
使用举例
根据文档来看,sorted() 会将流内的元素进行自然排序,同时要求元素必须实现 Comparable
接口。看到这里,问题就来了,什么是自然排序?为什么要实现 Comparable
接口?我们来实验一下。
public void sortedStringTest() { List<String> list = Arrays.asList("i", "am", "sorted"); System.out.println(list.stream().sorted().collect(Collectors.toList())); }
程序输出:
[am, i, sorted]
看来,确实按照字母的顺序来排序了。可是,如果元素没有实现 Comparable
又是什么情况呢?
public void sortedNotComparableTest() { List<StudentNotComparable> students = Arrays.asList(new StudentNotComparable("zhangsan", 15), new StudentNotComparable("lisi", 23)); students.stream().sorted().forEach(s -> System.out.println(s.getAge())); } public class StudentNotComparable { private String name; private int age; }
大事不妙,程序抛异常了:
Exception in thread “main” java.lang.ClassCastException: StudentNotComparable cannot be cast to java.lang.Comparable at java.util.Comparators$NaturalOrderComparator.compare(Comparators.java:47) at java.util.TimSort.countRunAndMakeAscending(TimSort.java:355) at java.util.TimSort.sort(TimSort.java:220) at java.util.Arrays.sort(Arrays.java:1512)
LowB 的我决定一探源码究竟。经过几步跳转,找到如下关键代码:
OfRef(AbstractPipeline<?, T, ?> upstream) { ...//此处省略 Comparator<? super T> comp = (Comparator<? super T>) Comparator.naturalOrder(); this.comparator = comp; }
好家伙,原来,排序过程中需要比较器。那从元素对象身上又是怎么取到比较器的呢?
enum NaturalOrderComparator implements Comparator<Comparable<Object>> { ...//此处省略 @Override public int compare(Comparable<Object> c1, Comparable<Object> c2) { return c1.compareTo(c2); } }
看来,猜测的没错了,
之所以需要元素对象实现 Comparable
,就是要用 compareTo
方法来排序
,这就是所谓的自然排序。
好了,明白了,我们再来看一下带有 Comparable
时运行结果。
public void sortedComparableTest() { List<StudentComparable> students = Arrays.asList(new StudentComparable("zhangsan", 15), new StudentComparable("lisi", 23)); students.stream().sorted().forEach(s -> System.out.println(s.getAge())); } public class StudentComparable implements Comparable<StudentComparable> { private String name; private int age; @Override public int compareTo(StudentComparable o) { return this.age - o.getAge(); } }
此时,运行正确,程序依次输出 15,23
两个数字。
sorted(Comparator<? super T> comparator)
经过了上面的原理探究,这个带有比较器方法的原理盲猜也可以猜到了:直接使用传入的比较器进行排序。来,试验一下。
public void sortedWithComparatorTest() { List<StudentNotComparable> students = Arrays.asList(new StudentNotComparable("zhangsan", 15), new StudentNotComparable("lisi", 23)); students.stream().sorted((s1, s2) -> s1.getAge() - s2.getAge()).forEach(s -> System.out.println(s.getAge())); }
果然,如果元素未实现 Comparable
接口,传入比较器之后,程序就正常运行了,依次输出 15,23
两个数字。
你以为至此就结束了吗?No,此时的我忽然冒出了另一个想法,如果元素实现了 Comparable
,此时又传入了比较器,排序时以哪个为准? 盲猜一下:
以传入的比较器为准,毕竟走的是传了比较器的方法。
public void sortedWithComparatorTest2() { List<StudentComparable> students = Arrays.asList(new StudentComparable("zhangsan", 15), new StudentComparable("lisi", 23)); students.stream().sorted((s1, s2) -> s2.getAge() - s1.getAge()).forEach(s -> System.out.println(s.getAge())); }
验证了一下,传入一个和 Comparable
相反的比较器,程序依次输出 23,15
两个数字,我们的盲猜是正确的。
为什么我能猜这么准?因为以前我们研究过 TreeMap
啊,原理都是一样的: Java 集合分析之 Map - 这个 Map 有顺序(LinkedHashMap 和 TreeMap)
peek
方法定义
Stream<T> peek(Consumer<? super T> action);
peek
方法可以将流元素取出并进行消费。看起来和 foreach
是一样,都传一个 Comsumer
,不同的是, peek
方法然后返回 Stream
,这也就意味着,我们对流进行取值,不影响流内元素继续流转,所以就可以用作 debug 了。
使用举例
public void peekTest() { List<String> list = Arrays.asList("i", "am", "sorted"); List<Integer> totalLength = list.stream().map(String::length).peek(System.out::println).collect(Collectors.toList()); System.out.println(totalLength); }
在这个过程中,我们先取出了各个字符串的长度并打印,最后,将所有长度转成一个 List,并最终打印。
1 2 6 [1, 2, 6]
总结
- sorted 方法可以对流内的元素进行排序
- 如果不传比较器,sorted 的元素需实现 Comparable,排序按照 compareTo 方法来排序
- 如果 sorted 方法传入了比较器,排序时以比较器为准
- 偷窃利器 peek 可以用于对流 debug,peek 不影响程序运行中间状态及最终结果
注:本文配套代码可在 github
查看: stream-and-lambda
- stream and lambda(18) - 终止操作之 stream 收集器 collectors
- stream and lambda(15) - 终止操作之 stream 数组操作 toArray
- stream and lambda(13) - 终止操作之计数 count 与比较 min、max
- stream and lambda(12) - 终止操作之查找与匹配(findAny、findFirst、allMatch、anyMatch、noneMatch)
- stream and lambda(11) - Optional 简介
- stream and lambda(10) - 中间操作之排序sorted与调试peek
- stream and lambda(9) - 中间操作之map操作(map、flatmap)
- stream and lambda(8) - 中间操作之筛选操作(filter、distinct、limit、skip)
- stream and lambda(7) - stream 的创建
- stream and lambda(6) - stream 简介
- stream and lambda(5) - lambda 表达式最佳实践
- stream and lambda(3) - jdk 提供的函数式接口
- stream and lambda(2) - 函数式接口简介
- stream and lambda(1) - lambda 简介