Stream 流的数据处理过程中,少不的一点就是对数据的筛选。在以前的编程方式里,对集合数据的筛选,要经过遍历、判断,搞不好过程中再遇到个异常,比如边遍历边 remove。而 Stream 提供的数据筛选方法,简化我们写法的同时,也可以有效避免复杂编码过程中不经意间写的 bug。
filter
方法定义
Stream<T> filter(Predicate<? super T> predicate);
filter
方法,通过传入的 Predicate
对流内的元素进行过滤,可以将不满足条件的元素过滤掉。
关于 Predicate
,我们也在函数式接口的内容里讲过:传送门
使用举例
1 | public void filterTest() { |
运行结果如下,此时已将元素过滤:
[23, 6]
distinct
方法定义
Stream<T> distinct();
distinct
方法,无入参,调用后将流内的重复元素去除。
使用举例
1 | public void distinctTest() { |
运行结果如下:
[1, 2, 4]
元素到底是怎么过滤的
虽然上面运行结果已经将元素过滤掉,但我们还是不清楚元素到底是怎么被过滤掉的。如果是对自定义对象类的元素过滤,我们可能无法有效预测过滤结果。
经过源代码查看,看到如下片断:
1 | <P_IN> Node<T> reduce(PipelineHelper<T> helper, Spliterator<P_IN> spliterator) { |
基本可以看出,去重是通过 Set
来去重的,而 Set
内部是通过 hashcode
和 equals
来去重。更详细的内容,可以参考以前的文章:Java集合分析之Set-以HashSet为例。
如果我们不想通过 hashcode
和 equals
去重怎么办呢?简单,可以用 ConcurrentHashMap
记录 key 是否已出现,配合 filter
方法来过滤。
limit
方法定义
Stream<T> limit(long maxSize);
limit
方法通过传入留取的最大数,来过滤流中的元素。
使用举例
1 | public void limitTest() { |
运行结果如下:
[1, 3, 5, 7]
skip
方法定义
Stream<T> skip(long n);
skip
方法通过传入跳过的元素数量,来过滤流中的元素。和 limit
刚好组成一对,一个掐头,一个去尾。
使用举例
1 | public void skipTest() { |
运行结果如下:
[7, 5]
总结
- 本次重点讲了
Stream
中间操作的元素筛选部分。 skip
和limit
,两个操作一个掐头,一个去尾。filter
根据传入的判断条件来过滤元素。distinct
根据hashcode
和equals
来去重。如果想根据其他内容来去重,可以使用filter
来实现。
注:本文配套代码可在
github
查看:stream-and-lambda