Stream API有哪些中间操作?看完你也可以吊打面试官!!

开发 后端
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值” 。Stream的中间操作是不会有任何结果数据输出的。

 作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址:

https://github.com/sunshinelyz/mykit-delay

Stream的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值” 。Stream的中间操作是不会有任何结果数据输出的。

Stream的中间操作在整体上可以分为:筛选与切片、映射、排序。接下来,我们就分别对这些中间操作进行简要的说明。

筛选与切片

这里,我将与筛选和切片有关的操作整理成如下表格。

 

接下来,我们列举几个简单的示例,以便加深理解。

为了更好的测试程序,我先构造了一个对象数组,如下所示。

  1. protected List<Employee> list = Arrays.asList( 
  2.     new Employee("张三", 18, 9999.99), 
  3.     new Employee("李四", 38, 5555.55), 
  4.     new Employee("王五", 60, 6666.66), 
  5.     new Employee("赵六", 8, 7777.77), 
  6.     new Employee("田七", 58, 3333.33) 
  7. ); 

其中,Employee类的定义如下所示。

  1. @Data 
  2. @Builder 
  3. @ToString 
  4. @NoArgsConstructor 
  5. @AllArgsConstructor 
  6. public class Employee implements Serializable { 
  7.     private static final long serialVersionUID = -9079722457749166858L; 
  8.     private String name
  9.     private Integer age; 
  10.     private Double salary; 

Employee类的定义比较简单,这里,我就不赘述了。之后的示例中,我们都是使用的Employee对象的集合进行操作。好了,我们开始具体的操作案例。

1.filter()方法

filter()方法主要是用于接收Lambda表达式,从流中排除某些元素,其在Stream接口中的源码如下所示。

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

可以看到,在filter()方法中,需要传递Predicate接口的对象,Predicate接口又是个什么鬼呢?点进去看下源码。

  1. @FunctionalInterface 
  2. public interface Predicate<T> { 
  3.  
  4.     boolean test(T t); 
  5.  
  6.     default Predicate<T> and(Predicate<? super T> other) { 
  7.         Objects.requireNonNull(other); 
  8.         return (t) -> test(t) && other.test(t); 
  9.     } 
  10.  
  11.     default Predicate<T> negate() { 
  12.         return (t) -> !test(t); 
  13.     } 
  14.  
  15.     default Predicate<T> or(Predicate<? super T> other) { 
  16.         Objects.requireNonNull(other); 
  17.         return (t) -> test(t) || other.test(t); 
  18.     } 
  19.  
  20.     static <T> Predicate<T> isEqual(Object targetRef) { 
  21.         return (null == targetRef) 
  22.                 ? Objects::isNull 
  23.                 : object -> targetRef.equals(object); 
  24.     } 

可以看到,Predicate是一个函数式接口,其中接口中定义的主要方法为test()方法,test()方法会接收一个泛型对象t,返回一个boolean类型的数据。

看到这里,相信大家明白了:filter()方法是根据Predicate接口的test()方法的返回结果来过滤数据的,如果test()方法的返回结果为true,符合规则;如果test()方法的返回结果为false,则不符合规则。

这里,我们可以使用下面的示例来简单的说明filter()方法的使用方式。

  1. //内部迭代:在此过程中没有进行过迭代,由Stream api进行迭代 
  2. //中间操作:不会执行任何操作 
  3. Stream<Person> stream = list.stream().filter((e) -> { 
  4.     System.out.println("Stream API 中间操作"); 
  5.     return e.getAge() > 30; 
  6. }); 

我们,在执行终止语句之后,一边迭代,一边打印,而我们并没有去迭代上面集合,其实这是内部迭代,由Stream API 完成。

下面我们来看看外部迭代,也就是我们人为得迭代。

  1. //外部迭代 
  2. Iterator<Person> it = list.iterator(); 
  3. while (it.hasNext()) { 
  4.     System.out.println(it.next()); 

2.limit()方法

主要作用为:截断流,使其元素不超过给定数量。

先来看limit方法的定义,如下所示。

  1. Stream<T> limit(long maxSize); 

limit()方法在Stream接口中的定义比较简单,只需要传入一个long类型的数字即可。

我们可以按照如下所示的代码来使用limit()方法。

  1. //过滤之后取2个值 
  2. list.stream().filter((e) -> e.getAge() >30 ).limit(2).forEach(System.out :: println); 

在这里,我们可以配合其他得中间操作,并截断流,使我们可以取得相应个数得元素。而且在上面计算中,只要发现有2条符合条件得元素,则不会继续往下迭代数据,可以提高效率。

3.skip()方法

跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补。

源码定义如下所示。

  1. Stream<T> skip(long n); 

源码定义比较简单,同样只需要传入一个long类型的数字即可。其含义是跳过n个元素。

简单示例如下所示。

  1. //跳过前2个值 
  2. list.stream().skip(2).forEach(System.out :: println); 

4.distinct()方法

筛选,通过流所生成元素的 hashCode() 和 equals() 去 除重复元素。

源码定义如下所示。

  1. Stream<T> distinct(); 

旨在对流中的元素进行去重。

我们可以如下面的方式来使用disinct()方法。

  1. list.stream().distinct().forEach(System.out :: println); 

这里有一个需要注意的地方:distinct 需要实体中重写hashCode()和 equals()方法才可以使用。

映射

关于映射相关的方法如下表所示。

 

1.map()方法

接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素。

先来看Java8中Stream接口对于map()方法的声明,如下所示。

  1. <R> Stream<R> map(Function<? super T, ? extends R> mapper); 

我们可以按照如下方式使用map()方法。

  1. //将流中每一个元素都映射到map的函数中,每个元素执行这个函数,再返回 
  2. List<String> list = Arrays.asList("aaa""bbb""ccc""ddd"); 
  3. list.stream().map((e) -> e.toUpperCase()).forEach(System.out::printf); 
  4.  
  5. //获取Person中的每一个人得名字name,再返回一个集合 
  6. List<String> names = this.list.stream().map(Person :: getName).collect(Collectors.toList()); 

2.flatMap()

接收一个函数作为参数,将流中的每个值都换成另 一个流,然后把所有流连接成一个流。

先来看Java8中Stream接口对于flatMap()方法的声明,如下所示。

  1. <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper); 

我们可以使用如下方式使用flatMap()方法,为了便于大家理解,这里,我就贴出了测试flatMap()方法的所有代码。

  1. /** 
  2.      * flatMap —— 接收一个函数作为参数,将流中的每个值都换成一个流,然后把所有流连接成一个流 
  3.      */ 
  4.     @Test 
  5.     public void testFlatMap () { 
  6.         StreamAPI_Test s = new StreamAPI_Test(); 
  7.         List<String> list = Arrays.asList("aaa""bbb""ccc""ddd"); 
  8.         list.stream().flatMap((e) -> s.filterCharacter(e)).forEach(System.out::println); 
  9.  
  10.         //如果使用map则需要这样写 
  11.         list.stream().map((e) -> s.filterCharacter(e)).forEach((e) -> { 
  12.             e.forEach(System.out::println); 
  13.         }); 
  14.     } 
  15.  
  16.     /** 
  17.      * 将一个字符串转换为流 
  18.      */ 
  19.     public Stream<Character> filterCharacter(String str){ 
  20.         List<Character> list = new ArrayList<>(); 
  21.         for (Character ch : str.toCharArray()) { 
  22.             list.add(ch); 
  23.         } 
  24.         return list.stream(); 
  25.     } 

其实map方法就相当于Collaction的add方法,如果add的是个集合得话就会变成二维数组,而flatMap 的话就相当于Collaction的addAll方法,参数如果是集合得话,只是将2个集合合并,而不是变成二维数组。

排序

关于排序相关的方法如下表所示。

 

从上述表格可以看出:sorted有两种方法,一种是不传任何参数,叫自然排序,还有一种需要传Comparator 接口参数,叫做定制排序。

先来看Java8中Stream接口对于sorted()方法的声明,如下所示。

  1. Stream<T> sorted(); 
  2. Stream<T> sorted(Comparator<? super T> comparator); 

sorted()方法的定义比较简单,我就不再赘述了。

我们也可以按照如下方式来使用Stream的sorted()方法。

  1. / 自然排序 
  2. List<Employee> persons = list.stream().sorted().collect(Collectors.toList()); 
  3.  
  4. //定制排序 
  5. List<Employee> persons1 = list.stream().sorted((e1, e2) -> { 
  6.     if (e1.getAge() == e2.getAge()) { 
  7.         return 0; 
  8.     } else if (e1.getAge() > e2.getAge()) { 
  9.         return 1; 
  10.     } else { 
  11.         return -1; 
  12.     } 
  13. }).collect(Collectors.toList()); 

写在最后

如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Java8新特性。

最后,附上Java8新特性核心知识图,祝大家在学习Java8新特性时少走弯路。

本文转载自微信公众号「冰河技术」,可以通过以下二维码关注。转载本文请联系冰河技术公众号。

 

责任编辑:武晓燕 来源: 冰河技术
相关推荐

2023-12-06 13:38:00

Redis缓存穿透缓存击穿

2023-02-20 08:08:48

限流算法计数器算法令牌桶算法

2019-04-26 14:12:19

MySQL数据库隔离级别

2021-09-27 06:50:04

非线性数据

2021-09-26 10:57:16

集合操作场景

2024-07-26 08:10:10

2021-08-20 08:33:19

操作系统OS

2024-03-07 17:21:12

HotSpotJVMHot Code

2024-03-12 14:36:44

微服务HTTPRPC

2024-04-19 00:00:00

计数器算法限流算法

2021-05-10 08:01:12

BeanFactoryFactoryBean容器

2024-02-26 14:07:18

2015-08-13 10:29:12

面试面试官

2021-08-11 08:53:23

Git命令面试

2024-02-01 08:08:53

Spring过滤器类型Gateway

2024-09-09 08:30:56

代码

2020-05-15 11:14:58

操作系统面试官运行

2022-02-14 20:53:33

开源库开发代码

2020-11-30 11:01:34

反射用途实现

2021-09-09 07:21:26

TypeScript 高级类型
点赞
收藏

51CTO技术栈公众号