概述
Stream API
把真正的函数式编程风格引入到java
中,这是目前为止对JAVA
类库最好的补充,因为其可以极大提供Java
程序员的生产力,让程序员写出高效率、干净、简洁的代码。
其是Java8
中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用其对集合数据进行操作,就类似于使用SQL
执行的数据库查询。也可以使用其来并行执行操作。简言之,其提供了一种高效且易于使用的处理数据的方式。
Stream
与Collection
集合的区别:Collection
是一种静态的内存数据结构,而Stream
是有关计算的,前者是主要面向内存,存储在内存中;后者主要面向CPU
,通过CPU
实现计算。
所以说Stream
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
注意:
-
Stream
自己不会存储元素 -
Stream
不会改变源对象,相反,他们会返回一个持有结果的新的Stream
-
Stream
操作是延迟执行的,这意味着他们会等到需要结构的时候才执行
Stream操作的三个步骤:
- 创建
Stream
:一个数据源,获取一个流 - 中间操作:中间操作链,对数据源的数据进行处理
- 终止操作:一旦执行终止操作,就执行中间操作链,并产生结果,之后,不会再被使用
实例化
Stream主要分为四种实例化方式:
- 通过集合 Stream
default Stream<E> parallelStream() 返回一个并行流,类似于线程,并行执行中间链
default Stream<E> stream() 返回一个顺序流,顺序执行中间链
- 通过数组 Stream
调用的是Arrays类的static <T> Stream<T> stream(<T>[] array)
- 通过
Stream.of()
方法 - 创建无限流
示例:
public class StreamNewTest {
/** 通过集合创建Stream */
public static void create1(){
//获取一个集合
List<User> users = UserData.getUsersData();
//default Stream<E> stream() 返回一个顺序流,顺序执行中间链
Stream<User> stream = users.stream();
//default Stream<E> parallelStream() 返回一个并行流,类似于线程,并行执行中间链
Stream<User> parallelStream = users.parallelStream();
}
/** 通过数组创建Stream */
public static void create2(){
//获取一个数组,静态初始化
int[] arr = new int[]{1,2,3,4,5,6};
//调用的是Arrays类的static <T> Stream<T> stream(<T>[] array)
IntStream s1 = Arrays.stream(arr);
//自定义数组
User[] arr2 = new User[]{new User("李四","28"),new User("赵六","28")};
Stream<User> s2 = Arrays.stream(arr2);
}
/** 通过Stream.of()创建Stream */
public static void create3(){
//不是数组形式,以包装类的形式
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
}
/** 创建Stream无限流 */
public static void create4(){
//迭代 static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
Stream.iterate(0,t -> t+2).limit(10).forEach(System.out::println);
//生成 static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(()->2).limit(10).forEach(System.out::println);
}
}
切片与筛选
方法 | 描述 |
| 接收 |
| 筛选,通过流所生成元素的 |
| 截断流,使其元素不超过给定数量 |
| 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,返回一个空流,与limit()互补 |
示例:
/** 切片与筛选 */
public static void test1(){
List<User> list = UserData.getUsersData();
//filter(Predicate p) 筛选
list.stream().filter(u -> Integer.parseInt(u.getAge()) > 20).forEach(System.out::println);
//distinct() 去重
list.stream().distinct().forEach(System.out::println);
//limit(long maxSize) 切片,截取流的前几个元素
list.stream().limit(2).forEach(System.out::println);
//skip(long n) 切片,跳过流的前n个元素
list.stream().skip(2).forEach(System.out::println);
}
映射
方法 | 描述 |
| 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。 |
| 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 |
| 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 |
| 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 |
| 接收一个函数作为参数,将流中的每个值都被换成另一个流,然后把所有流连接成一个流。 |
示例:
/** 映射 */
public static void test2(){
//map(Function f) 遍历每个元素,并将其映射成一个新的元素
List<String> list = Arrays.asList("20", "1", "2", "3");
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
//mapToInt(ToIntFunction f);
list.stream().mapToInt(str -> Integer.parseInt(str)).forEach(System.out::println);
//flatMap(Function f) 流中的每个值都被换成另一个流,然后把所有流连接成一个流
list.stream().flatMap(StreamApiTest::fromStrToStream).forEach(System.out::println);
//对比map方法实现
Stream<Stream<Character>> streamStream = list.stream().map(StreamApiTest::fromStrToStream);
streamStream.forEach(s -> {
s.forEach(System.out::println);
});
}
public static Stream<Character> fromStrToStream(String str){
ArrayList<Character> list = new ArrayList<>();
for(Character c :str.toCharArray()){
list.add(c);
}
return list.stream();
}
排序
方法 | 描述 |
| 产生一个新流,其中按自然顺序排序 |
| 产生一个新流,其中按比较器顺序排序 |
示例:
/** 排序 */
public static void test3(){
List<User> list = UserData.getUsersData();
List<Integer> list1 = Arrays.asList(1, 2, 3, 4, 5, 6);
//自然顺序排序
list1.stream().sorted().forEach(System.out::println);
//按比较器顺序排序,对象排序需要是实现Comparator接口或者如下
list.stream().sorted((u1,u2) -> Integer.compare(Integer.parseInt(u1.getAge()),Integer.parseInt(u2.getAge()))).forEach(System.out::println);
}
匹配与查找
方法 | 描述 |
| 检查是否匹配所有元素 |
| 检查是否至少匹配一个元素 |
| 检查是否没有匹配所有元素 |
| 返回第一个元素 |
| 返回当前流中的任意元素 |
| 统计总数 |
| 返回流中最大值 |
| 返回流中最小值 |
| 内部迭代 |
示例:
/** 匹配与查找 */
public static void test4(){
List<User> list = UserData.getUsersData();
//allMatch(Predicate p):断定型判断,返回布尔类型,流中是否所有用户都是50岁以上
boolean b = list.stream().allMatch(u -> Integer.parseInt(u.getAge()) > 50);
//anyMatch(Predicate p):断定型判断,返回布尔类型,流中是否存在用户是50岁以上
boolean b1 = list.stream().anyMatch(u -> Integer.parseInt(u.getAge()) > 50);
//anyMatch(Predicate p):断定型判断,返回布尔类型,流中是否不存在用户是50岁以上
boolean b2 = list.stream().noneMatch(u -> Integer.parseInt(u.getAge()) > 50);
//findFirst():返回第一个元素
Optional<User> u = list.stream().findFirst();
//findAny():返回任一个元素
Optional<User> u1 = list.stream().findAny();
//count():统计总数
long count = list.stream().count();
//max(Comparator c):返回流中最大值
Optional<User> maxUser = list.stream().max((o1, o2) -> Integer.compare(Integer.parseInt(o1.getAge()), Integer.parseInt(o2.getAge())));
//min(Comparator c):返回流中最小值
Optional<User> minUser = list.stream().min((o1, o2) -> Integer.compare(Integer.parseInt(o1.getAge()), Integer.parseInt(o2.getAge())));
//forEach(Consumer c):内部迭代
list.stream().forEach(System.out::println);
}
归约
方法 | 描述 |
| 可以将流中元素反复结合起来,得到一个值,返回T |
| 可以将流中元素反复结合起来,得到一个值,返回Optional |
示例:
/** 归约 */
public static void test5(){
//reduce(T iden, BinaryOperator b):计算总和
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer reduce = list.stream().reduce(0, Integer::sum);
//reduce(BinaryOperator b):计算所有用户的总年龄
List<User> uList = UserData.getUsersData();
Optional<User> reduce1 = uList.stream().reduce((u1, u2) -> new User("", Integer.parseInt(u1.getAge()) + Integer.parseInt(u2.getAge())+""));
//reduce(BinaryOperator b):计算所有用户的总年龄2--->map-reduce
OptionalInt reduce2 = uList.stream().mapToInt(u -> Integer.parseInt(u.getAge())).reduce(Integer::sum);
}
收集
方法 | 描述 |
| 将流转换为其他形式,接收一个 |
示例:
/** 收集 */
public static void test6(){
List<User> uList = UserData.getUsersData();
//不使用collector返回的是一个流
Stream<User> userStream = uList.stream().filter(u -> Integer.parseInt(u.getAge()) > 50);
//使用collector返回的是一个List
List<User> users = uList.stream().filter(u -> Integer.parseInt(u.getAge()) > 50).collect(Collectors.toList());
}
Collectors方法简单介绍:
方法 | 描述 |
| 将流中的元素全部放置到一个集合中返回 |
| 将流中的元素放置到一个列表集合 |
| 将流中的元素放置到一个无序集 |
| 将流中的元素全部以字符序列的方式连接到一起,可以指定连接符,甚至是结果的前后缀 |
| 这个映射是首先对流中的每个元素进行映射,即类型转换,然后再将新元素以给定的 |
| 在归纳动作结束之后,对归纳的结果进行再处理 |
| 用于计数 |
| 生成一个用于获取最小/最大值的 |
| 求和,除此外还包含求平均数等 |
| 根据给定的键生成器和值生成器生成的键和值保存到一个 |
| 生成一个拥有分组功能的 |
| 适用于汇总的,返回值分别是 |