0%

stream and lambda(8) - 中间操作之筛选操作(filter、distinct、limit、skip)

Stream 流的数据处理过程中,少不的一点就是对数据的筛选。在以前的编程方式里,对集合数据的筛选,要经过遍历、判断,搞不好过程中再遇到个异常,比如边遍历边 remove。而 Stream 提供的数据筛选方法,简化我们写法的同时,也可以有效避免复杂编码过程中不经意间写的 bug。

filter

方法定义

Stream<T> filter(Predicate<? super T> predicate);

filter 方法,通过传入的 Predicate 对流内的元素进行过滤,可以将不满足条件的元素过滤掉。

关于 Predicate ,我们也在函数式接口的内容里讲过:传送门

使用举例

1
2
3
4
5
public void filterTest() {
Stream<Integer> stream = Stream.of(1, 23, 4, 5, 6);
List<Integer> result = stream.filter(i -> i > 5).collect(Collectors.toList());
System.out.println(result);
}

运行结果如下,此时已将元素过滤:

[23, 6]

distinct

方法定义

Stream<T> distinct();

distinct 方法,无入参,调用后将流内的重复元素去除。

使用举例

1
2
3
4
5
public void distinctTest() {
Stream<Integer> stream = Stream.of(1, 1, 2, 2, 4);
List<Integer> result = stream.distinct().collect(Collectors.toList());
System.out.println(result);
}

运行结果如下:

[1, 2, 4]

元素到底是怎么过滤的

虽然上面运行结果已经将元素过滤掉,但我们还是不清楚元素到底是怎么被过滤掉的。如果是对自定义对象类的元素过滤,我们可能无法有效预测过滤结果。

经过源代码查看,看到如下片断:

1
2
3
4
5
6
<P_IN> Node<T> reduce(PipelineHelper<T> helper, Spliterator<P_IN> spliterator) {
TerminalOp<T, LinkedHashSet<T>> reduceOp
= ReduceOps.<T, LinkedHashSet<T>>makeRef(LinkedHashSet::new, LinkedHashSet::add,
LinkedHashSet::addAll);
return Nodes.node(reduceOp.evaluateParallel(helper, spliterator));
}

基本可以看出,去重是通过 Set 来去重的,而 Set 内部是通过 hashcodeequals 来去重。更详细的内容,可以参考以前的文章:Java集合分析之Set-以HashSet为例

如果我们不想通过 hashcodeequals 去重怎么办呢?简单,可以用 ConcurrentHashMap 记录 key 是否已出现,配合 filter 方法来过滤。

limit

方法定义

Stream<T> limit(long maxSize);

limit 方法通过传入留取的最大数,来过滤流中的元素。

使用举例

1
2
3
4
5
public void limitTest() {
Stream<Integer> stream = Stream.of(1, 3, 5, 7, 5);
List<Integer> result = stream.limit(4).collect(Collectors.toList());
System.out.println(result);
}

运行结果如下:

[1, 3, 5, 7]

skip

方法定义

Stream<T> skip(long n);

skip 方法通过传入跳过的元素数量,来过滤流中的元素。和 limit 刚好组成一对,一个掐头,一个去尾。

使用举例

1
2
3
4
5
public void skipTest() {
Stream<Integer> stream = Stream.of(1, 3, 5, 7, 5);
List<Integer> result = stream.skip(3).collect(Collectors.toList());
System.out.println(result);
}

运行结果如下:

[7, 5]

总结

  • 本次重点讲了 Stream 中间操作的元素筛选部分。
  • skiplimit ,两个操作一个掐头,一个去尾。
  • filter 根据传入的判断条件来过滤元素。
  • distinct 根据 hashcodeequals 来去重。如果想根据其他内容来去重,可以使用 filter 来实现。

注:本文配套代码可在 github 查看:stream-and-lambda