Java 8中提供了一个新的附加程序包,称为java.util.stream。该软件包由类,接口和枚举组成,以允许对元素进行功能样式的操作。
可以使用流来过滤,收集,打印以及从一种数据结构转换为另一种数据结构等。
流Stream,可以让你以一种声明的方式处理数据。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
Stream(流)是一个来自数据源的元素队列并支持聚合操作
和以前的Collection操作不同, Stream操作还有两个基础的特征:
Stream提供以下功能:
在 Java 8 中, 集合接口有两个方法来生成流:
stream() − 为集合创建串行流,由主线程按顺序对流执行操作。
parallelStream() − 为集合创建并行流,内部以多线程并行执行的方式对流进行操作。
除了直接创建并行流,还可以通过 parallel()把顺序流转换成并行流
1、通过Collection系列集合提供的stream()或parallelStream()
List list = new ArrayList<>();
Stream stream = list.stream(); //顺序流
Stream stringStream = list.parallelStream(); //并行流
2、 使用Arrays 中的 stream() 方法,将数组转成流
Integer[] nums = new Integer[]{1,2,3,4};
Stream Integerstream = Arrays.stream(nums);
3、使用Stream中的静态方法:of()、iterate()、generate()
Stream TestStream = Stream.of(1,2,3,4);
//iterate()、generate()方法可以创建无限流,可以通过limit()方法来限制数量。
Stream TestStream2 = Stream.iterate(0,x->x+2);
Stream TestStream3 = Stream.generate(()-> new Random().nextInt(10));
Stream TestStream4 = Stream.iterate(0,x->x+2).limit(10);
4、使用 BufferedReader.lines() 方法,将每行内容转成流
BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream lineStream = reader.lines();
//上面可以写成一行
Stream lineStream2 = new BufferedReader(new FileReader("F:\\test_stream.txt")).lines();
5、使用 Pattern.splitAsStream() 方法,将字符串分隔成流
Pattern pattern = Pattern.compile(",");
Stream stringStream2 = pattern.splitAsStream("a,b,c,d");
6、数值范围生成流
// 生成0到10的int流
IntStream intStream = IntStream.rangeClosed(0, 10);
// 生成0到9的int流
IntStream intStream1 = IntStream.range(0, 10);
中间操作(Intermediate):可以有多个,每次返回一个新的流,可进行链式操作,其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用,仅仅调用到这类方法,并没有真正开始流的遍历。
终端操作(Terminal):只能有一个,每次执行完,这个流也就用光光了,无法执行下一个操作,因此只能放在最后,Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个side effect。
Stream操作分类 | |||
---|---|---|---|
中间操作 | 无状态 | filter map flatMap peek | 指元素的处理不受之前元素的影响 |
有状态 | distinct sorted limit skip | 指该操作只有拿到所有元素之后才能继续下去 | |
结束操作 | 非短路操作 | forEach reduce collect max min count | 指必须处理所有元素才能得到最终结果 |
短路操作 | anyMatch allMatch noneMatch findFirst findAny | 指遇到某些符合条件的元素就可以得到最终结果 |
filter:返回一个通过设置的条件过滤出元素的流
distinct: 通过流中元素的 hashCode() 和 equals() 去除重复元素
limit(n): 返回一个不超过给定长度的流。
skip(n): 返回一个跳过前n个元素的流,可配合limit(n)可实现分页。
sorted : 返回正序排序后的流
// 过滤所有空字符串,注意是返回 true的留下
Stream.of("1", null, "2", "", "3").filter(StringUtils::isNotEmpty).forEach(System.out::print);// 去掉重复的2
Stream.of("1", "2", "2", "2", "3").distinct().forEach(System.out::print);// 跳过前两个元素
Stream.of("1", "2", "3", "4", "5").skip(2).forEach(System.out::print);// 与skip相反,只留下前2个
Stream.of("1", "2", "3", "4", "5").limit(2).forEach(System.out::print);//排序
List strings = Arrays.asList(10, 2, 30, 22, 1,0, -9);
List sorted = strings.stream().sorted().collect(Collectors.toList());
map: 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap: 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
List list = Arrays.asList("a,b,c", "1,2,3");//将每个元素转成一个新的且不带逗号的元素Stream s1 = list.stream().map(s -> s.replaceAll(",", ""));s1.forEach(System.out::println); // abc 123Stream s3 = list.stream().flatMap(s -> {//将每个元素转换成一个streamString[] split = s.split(",");Stream s2 = Arrays.stream(split);return s2;});s3.forEach(System.out::println); // a b c 1 2 3
匹配、聚合操作
allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
findFirst:返回流中第一个元素
findAny:返回流中的任意元素
count:返回流中元素的总个数
max:返回流中元素最大值
min:返回流中元素最小值
List list = Arrays.asList(1, 2, 3, 4, 5); boolean allMatch = list.stream().allMatch(e -> e > 10); //false
boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
boolean anyMatch = list.stream().anyMatch(e -> e > 4); //true Integer findFirst = list.stream().findFirst().get(); //1
Integer findAny = list.stream().findAny().get(); //1 long count = list.stream().count(); //5
Integer max = list.stream().max(Integer::compareTo).get(); //5
Integer min = list.stream().min(Integer::compareTo).get(); //1
规约操作
T reduce(T identity, BinaryOperator accumulator) :可以将流中的元素反复结合起来,得到一个值。返回T。
Optional reduce(BinaryOperator accumulator) :可以将流中的元素反复结合起来,得到一个值。返回Optional。
//计算1-10的自然数的和
List list= Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer sum = list.stream().reduce(0, Integer::sum);
System.out.println(sum);
//计算公司员工总工资
Stream doubleStream = emps.stream().map(Employee::getSalary);
Optional sumMoney = doubleStream.reduce(Double::sum);
System.out.println(sumMoney);
collect 收集器,将流转换为其他形式。
List strings = Arrays.asList("cv", "abd", "aba", "efg", "abcd","jkl", "jkl");
Set set = strings.stream().collect(Collectors.toSet());
List list = strings.stream().collect(Collectors.toList());
//v.concat("_name"):键,v1 -> v1:值
Map map = strings.stream().collect(Collectors.toMap(v ->v.concat("_name"), v1 -> v1, (v1, v2) -> v1));
Collector
Supplier < A>supplier():创建一个结果容器A
BiConsumer accumulator():消费型接口,第一个参数为容器A,第二个参数为流中元素T。
BinaryOperator< A> combiner():函数接口,该参数的作用跟上一个方法(reduce)中的combiner参数一样,将并行流中各 个子进程的运行结果(accumulator函数操作后的容器A)进行合并。
Function finisher():函数式接口,参数为:容器A,返回类型为:collect方法最终想要的结果R。
Set characteristics():返回一个不可变的Set集合,用来表明该Collector的特征。有以下三个特征:
CONCURRENT:表示此收集器支持并发。
UNORDERED:表示该收集操作不会保留流中元素原有的顺序。
IDENTITY_FINISH:表示finisher参数只是标识而已,可忽略。
Person p1 = new Person("zhangsan",26);
Person p2 = new Person("lisi",22);
Person p3 = new Person("wangwu",23);
List list = Arrays.asList(p1,p2,p3);
//装成list
List ageList = list.stream().map(Person::getAge).collect(Collectors.toList());//[26,22,22]
//转成set
Set ageSet = list.stream().map(Person::getAge).collect(Collectors.toSet());//[26,22]//转成map,注:key不能相同,否则报错
Map studentMap = list.stream().collect(Collectors.toMap(Person::getName, Person::getAge));
// {zhangsan=26, lisi=22, wangwu=22}//字符串分隔符连接
String joinName = list.stream().map(Person::getName).collect(Collectors.joining(",", "(", ")"));
// (zhangsan,lisi,wangwu)//聚合操作
//1.总数
Long count = list.stream().collect(Collectors.counting()); // 3
//2.最大年龄 (最小的minBy同理)
Integer maxAge = list.stream().map(Person::getAge).collect(Collectors.maxBy(Integer::compare)).get(); // 26
//3.所有人的年龄求和
Integer sumAge = list.stream().collect(Collectors.summingInt(Person::getAge)); // 70
//4.平均年龄
Double averageAge = list.stream().collect(Collectors.averagingDouble(Person::getAge)); // 23.333333333333332
// 带上以上所有方法
DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Person::getAge));
System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());//分组 按年龄分组
Map> ageMap = list.stream().collect(Collectors.groupingBy(Person::getAge));
//分区
//分成两部分,一部分大于10岁,一部分小于等于10岁
Map> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
//规约
Integer allAge = list.stream().map(Person::getAge).collect(Collectors.reducing(Integer::sum)).get(); //40