1.Stream 概念
Stream(流)是一个来自数据源的元素队列并支持聚合操作
- 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
和以前的Collection操作不同, Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现
2.流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等
+--------------------+ +------+ +------+ +---+ +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+ +------+ +------+ +---+ +-------+
3.创建流
在 Java 8 中, 集合接口有两个方法来生成流:
-
stream() − 为集合创建串行流。
-
parallelStream() − 为集合创建并行流。
3.1 通过集合生成,应用中最常用的一种
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6);
Stream<Integer> stream = integerList.stream();
3.2 通过数组生成
int[] intArr = {1, 2, 3, 4, 5, 6};
IntStream stream = Arrays.stream(intArr);
通过Arrays.stream方法生成流,并且该方法生成的流是数值流【即IntStream】而不是 Stream。补充一点使用数值流可以避免计算过程中拆箱装箱,提高性能。
Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream 】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流.
3.3 通过值生成
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
通过Stream的of方法生成流,通过Stream的empty方法可以生成一个空流.
3.4 通过文件生成
Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行.
3.5 通过函数生成 iterator
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数
3.6 steam.generator
Stream<Double> stream = Stream.generate(Math::random).limit(5);
generate方法接受一个参数,方法参数类型为Supplier ,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断。
4.流的操作类型
流的操作类型主要分为两种
- 中间操作
一个流可以后面跟随零个或多个中间操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的,仅仅调用到这类方法,并没有真正开始流的遍历,真正的遍历需等到终端操作时,常见的中间操作有下面即将介绍的 filter、map 等。
- 终端操作
一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流。终端操作的执行,才会真正开始流的遍历。如下面即将介绍的 count、collect 等。
5.流的使用
5.1 中间操作
5.1.1 filter 筛选
通过使用filter方法进行条件筛选,filter的方法参数为一个条件(过滤保留函数返回值为 true 的元素)。
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6);
Stream<Integer> stream = integerList.stream().filter(i -> i > 3);
结果为:4,5,6
5.1.2 distinct 去重
通过distinct方法快速去除重复的元素。
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().distinct();
结果为:1,2,3,4,5
5.1.3 limit 返回指定流个数
通过limit方法指定返回流的个数,limit的参数值必须 >=0,否则将会抛出异常。
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().limit(3);
结果为: 1,1,2
5.1.4 skip 跳过流中的元素
通过skip方法跳过流中的元素。
List<Integer> integerList = Arrays.asList(1, 1, 2, 3, 4, 5);
Stream<Integer> stream = integerList.stream().skip(2);
5.1.5 map 流映射
所谓流映射就是将接受的元素映射成另外一个元素。
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
List<Integer> collect = stringList.stream()
.map(String::length)
.collect(Collectors.toList());
结果为:[6, 7, 2, 6]
通过map方法可以完成映射,该例子完成中 i -> stringList.length() 的映射。
5.1.5 flatMap 流转换
将一个流中的每个值都转换为另一个流.
List<String> wordList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
List<String> strList = wordList.stream()
.map(w -> w.split(" "))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());
结果为:[Java, 8, Lambdas, In, Action]
map(w -> w.split(" ")) 的返回值为 Stream<String[]>,想获取 Stream,可以通过flatMap方法完成 Stream ->Stream 的转换。
5.1.6 allMatch 匹配所有元素
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().allMatch(i -> i > 3)) {
System.out.println("所有元素值都大于3");
} else {
System.out.println("并非所有元素值都大于3");
}
5.1.7 anyMatch匹配其中一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().anyMatch(i -> i > 3)) {
System.out.println("存在值大于3的元素");
} else {
System.out.println("不存在值大于3的元素");
}
等同于
for (Integer i : integerList) {
if (i > 3) {
System.out.println("存在大于3的值");
break;
}
}
5.1.8 noneMatch全部不匹配
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
if (integerList.stream().noneMatch(i -> i > 3)) {
System.out.println("值都小于3的元素");
} else {
System.out.println("值不都小于3的元素");
}
5.2 终端操作
5.2.1 count 统计流中元素个数
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Long result = integerList.stream().count();
结果为:5
5.2.1 findFirst 查找第一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findFirst();
System.out.println(result.orElse(-1));
结果为:4
5.2.2 findAny 随机查找一个
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Optional<Integer> result = integerList.stream().filter(i -> i > 3).findAny();
System.out.println(result.orElse(-1));
结果为:4
通过findAny方法查找到其中一个大于三的元素并打印,因为内部进行优化的原因,当找到第一个满足大于三的元素时就结束,该方法结果和findFirst方法结果一样。提供findAny方法是为了更好的利用并行流,findFirst方法在并行上限制更多
5.2.3 reduce 将流中的元素组合
- 用于求和:
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
int sum = integerList.stream()
.reduce(0, Integer::sum);
结果为:15
等同于
int sum = 0;
for (int i : integerList) {
sum += i;
}
演变
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
//------------------------------------------------------------------------------------------------------
// int sum = integerList.stream()
// .reduce(20, new BinaryOperator<Integer>() {
// @Override
// public Integer apply(Integer integer, Integer integer2) {
// System.out.println(integer + ";" + integer2);
// return integer + integer2;
// }
// });
//------------------------------------------------------------------------------------------------------
// int sum = integerList.stream()
// .reduce(20, (integer, integer2) -> {
// System.out.println(integer + ";" + integer2);
// return integer + integer2;
// });
//------------------------------------------------------------------------------------------------------
// int sum = integerList.stream()
// .reduce(20, (integer, integer2) -> {
// System.out.println(integer + ";" + integer2);
// return Integer.sum(integer,integer2);
// });
//------------------------------------------------------------------------------------------------------
// int sum = integerList.stream()
// .reduce(20, (integer, integer2) -> integer + integer2);
//------------------------------------------------------------------------------------------------------
int sum = integerList.stream()
.reduce(20, Integer::sum);
System.out.println("最终结果为:------" + sum);
最终结果为:------35
reduce接受两个参数,一个初始值这里是0,一个 BinaryOperatoraccumulator来将两个元素结合起来产生一个新值,另外reduce方法还有一个没有初始化值的重载方法。
- 用于获取最大最小值
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
Optional<Integer> min = stringList.stream()
.map(String::length)
.reduce(Integer::min);
Optional<Integer> max = stringList.stream()
.map(String::length)
.reduce(Integer::max);
结果为:Optional[2] 和 Optional[7]
5.2.4 min/max 获取最小最大值
方法参数为 Comparator<?superT>comparator
- 写法1:
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
Optional<Integer> min = stringList.stream()
.map(String::length)
.min(Integer::compareTo);
Optional<Integer> max = stringList.stream()
.map(String::length)
.max(Integer::compareTo);
结果为:Optional[2] 和 Optional[7]
- 写法2:
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
OptionalInt
min = stringList.stream()
.mapToInt(String::length)
.min();
OptionalInt
max = stringList.stream()
.mapToInt(String::length)
.max();
- 写法3: 使用reduce获取最大最小值
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
Optional<Integer> min = stringList.stream()
.map(String::length)
.reduce(Integer::min);
Optional<Integer> max = stringList.stream()
.map(String::length)
.reduce(Integer::max);
5.2.5 sum / summingxxx / reduce 求和
求和的实现方式有很多种如下:
- 方式1:sum(推荐)
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
int sum = stringList.stream()
.mapToInt(String::length)
.sum();
- 方式2:summingInt
如果数据类型为double、long,则通过summingDouble、summingLong方法进行求和。
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
int sum = stringList.stream()
.collect(summingInt(String::length));
- 方式3:reduce
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
int sum = stringList.stream()
.map(String::length)
.reduce(0, Integer::sum);
在上面求和、求最大值、最小值的时候,对于相同操作有不同的方法可以选择执行。可以选择collect、reduce、min/max/sum方法,推荐使用min、max、sum方法。因为它最简洁易读,同时通过mapToInt将对象流转换为数值流,避免了装箱和拆箱操作
5.2.6 averagingxxx 求平均值
static int num = 0;
static void test_4(){
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
// double average = stringList.stream()
// .collect(averagingInt(String::length));
double average = stringList.stream()
.collect(Collectors.averagingInt(new ToIntFunction<String>() {
@Override
public int applyAsInt(String value) {
System.out.println(value.length());
num += value.length();
return value.length();
}
}));
System.out.println("----" + new BigDecimal(num).divide(new BigDecimal(stringList.size())));
System.out.println(average);
}
//----------------------------------------------------------------------------------
6
7
2
6
----5.25
5.25
5.2.7 summarizingxxx 同时求总和、平均值、最大值、最小值
如果数据类型为double、long,则通过summarizingDouble、summarizingLong方法
static void test_5(){
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
IntSummaryStatistics intSummaryStatistics = stringList.stream()
.collect(Collectors.summarizingInt(String::length));
double average = intSummaryStatistics.getAverage(); // 获取平均值
int min = intSummaryStatistics.getMin(); // 获取最小值
int max = intSummaryStatistics.getMax(); // 获取最大值
long sum = intSummaryStatistics.getSum(); // 获取总和
System.out.println(average);
System.out.println(min);
System.out.println(max);
System.out.println(sum);
}
//---------------------------------------------------------------------------------------------
5.25
2
7
21
5.2.8 forEach 遍历
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
stringList.stream().forEach(System.out::println);
5.2.9 collect 返回集合
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
List<Integer> intList = stringList.stream()
.map(String::length)
.collect(toList());
Set<Integer> intSet = stringList.stream()
.map(String::length)
.collect(toSet());
5.2.10 joining 拼接流中的元素
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
String result = stringList.stream()
.map(String::toLowerCase)
.collect(Collectors.joining("-"));
结果为:java 8-lambdas-in-action
5.2.11 groupingBy 分组
在collect方法中传入groupingBy进行分组,其中groupingBy的方法参数为分类函数。
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
Map<Integer, List<String>> collect = stringList.stream().collect(groupingBy(String::length));
结果为:{2=[In], 6=[Java 8, Action], 7=[Lambdas]}
还可以通过嵌套使用groupingBy进行多级分类
List<String> stringList = Arrays.asList("Java 12", "Lambdas", "In", "Action");
Map<Integer, Map<Integer, List<String>>> collect = stringList.stream()
.collect(groupingBy(
String::length,
groupingBy(String::hashCode)
));
结果为:
{2={2373=[In]}, 6={1955883606=[Action]}, 7={1611513196=[Lambdas], -155279169=[Java 12]}}
List<String> stringList = Arrays.asList("Java 12", "Lambdas", "In", "Action");
Map<Integer, Map<String, List<String>>> collect = stringList.stream()
.collect(groupingBy(
String::length,
groupingBy(item -> {
if (item.length() <= 2) {
return "level1";
} else if (item.length() <= 6) {
return "level2";
} else {
return "level3";
}
})
));
结果为:{2={level1=[In]}, 6={level2=[Action]}, 7={level3=[Java 12, Lambdas]}}
5.2.12 partitioningBy 分区
分区是特殊的分组,它分类依据是true和false,所以返回的结果最多可以分为两组。
List<String> stringList = Arrays.asList("Java 12", "Lambdas", "In", "Action");
Map<Boolean, List<String>> collect = stringList.stream().collect(partitioningBy(String::isEmpty));
结果为:{false=[Java 12, Lambdas, In, Action], true=[]}
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
Map<Boolean, List<Integer>> result = integerList.stream().collect(partitioningBy(i -> i < 3));
结果为:{false=[3, 4, 5], true=[1, 2]}
6. 代码:
package com.zhangmeng.mystyle.tools;
import javax.sound.midi.Soundbank;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.stream.Collectors.averagingInt;
import static java.util.stream.Collectors.groupingBy;
/**
* @author zhangmeng
* @version 1.0
* @date 2022年5月6日09:34:24
*/
public class SteamTools {
/**
* #### 3.1 通过集合生成,应用中最常用的一种
* <p>
* ```
* List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6);
* Stream<Integer> stream = integerList.stream();
* ```
* <p>
* #### 3.2 通过数组生成
* <p>
* ```
* int[] intArr = {1, 2, 3, 4, 5, 6};
* IntStream stream = Arrays.stream(intArr);
* ```
* <p>
* >通过Arrays.stream方法生成流,并且该方法生成的流是数值流【即IntStream】而不是 Stream。补充一点使用数值流可以避免计算过程中拆箱装箱,提高性能。
* <p>
* > Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream 】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流.
* <p>
* <p>
* #### 3.3 通过值生成
* <p>
* ```
* Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
* ```
* > 通过Stream的of方法生成流,通过Stream的empty方法可以生成一个空流.
* <p>
* #### 3.4 通过文件生成
* <p>
* ```
* Stream<String> lines = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
* ```
* > 通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行.
* <p>
* #### 3.5 通过函数生成 iterator
* <p>
* ```
* Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(5);
* ```
* > iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数
* <p>
* #### 3.6 steam.generator
* ```
* Stream<Double> stream = Stream.generate(Math::random).limit(5);
* ```
* > generate方法接受一个参数,方法参数类型为Supplier ,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断。
*/
public static void main(String[] args) {
test_6();
}
static void test_7(){
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
// Map<Integer, List<String>> collect = stringList.stream().collect(groupingBy(String::length));
Map<Object, List<String>> collect = stringList.stream().collect(groupingBy(new Function<String, Object>() {
@Override
public Object apply(String s) {
System.out.println(s);
return s.length();
}
}));
System.out.println(collect);
}
static void test_6(){
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
Map<Integer, Map<Integer, List<String>>> collect = stringList.stream()
.collect(groupingBy(
i -> i.length(),
groupingBy(String::hashCode)
));
System.out.println(collect);
}
static void test_5(){
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
IntSummaryStatistics intSummaryStatistics = stringList.stream()
.collect(Collectors.summarizingInt(String::length));
double average = intSummaryStatistics.getAverage(); // 获取平均值
int min = intSummaryStatistics.getMin(); // 获取最小值
int max = intSummaryStatistics.getMax(); // 获取最大值
long sum = intSummaryStatistics.getSum(); // 获取总和
System.out.println(average);
System.out.println(min);
System.out.println(max);
System.out.println(sum);
}
static int num = 0;
static void test_4(){
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
// double average = stringList.stream()
// .collect(averagingInt(String::length));
double average = stringList.stream()
.collect(Collectors.averagingInt(new ToIntFunction<String>() {
@Override
public int applyAsInt(String value) {
System.out.println(value.length());
num += value.length();
return value.length();
}
}));
System.out.println("----" + new BigDecimal(num).divide(new BigDecimal(stringList.size())));
System.out.println(average);
}
static void test_3(){
//需求7: 挑选出得分最高/最低的学生名 (max、min)
Optional<Student> max = list.stream().max((student1, student2) -> (int) (student1.getScore() - student2.getScore()));
System.out.println(max.orElse(null).id);
Optional<Student> min = list.stream().min((student1, student2) -> (int) (student1.getScore() - student2.getScore()));
System.out.println(min.orElse(null).id);
// //需求8: 遍历(forEach、peek)
// // 遍历既可以用forEach、也可以用peek,作用相似,都是接受一个消费型的函数接口
// list.stream().peek(System.out::println).forEach(System.out::println);
//
// //需求9:随便来一个90分及以上的
// Optional<Student> any = list.stream().filter(student -> student.getScore() >= 90L).findAny();
// System.out.println(any.orElse(null));
//
}
static void test_2(){
List<String> stringList = Arrays.asList("Java 8", "Lambdas", "In", "Action");
Optional<Integer> min =
stringList.stream()
.map(String::length).reduce(Integer::min);
Optional<Integer> max = stringList.stream()
.map(String::length)
.reduce(Integer::max);
System.out.println(min.orElse(-1));
System.out.println(max.orElse(-1));
}
static void test_1() {
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5);
int sum = integerList.stream()
.reduce(20, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
System.out.println(integer + ";" + integer2);
return integer + integer2;
}
});
// int sum = integerList.stream()
// .reduce(20, (integer, integer2) -> {
// System.out.println(integer + ";" + integer2);
// return integer + integer2;
// });
// int sum = integerList.stream()
// .reduce(20, (integer, integer2) -> {
// System.out.println(integer + ";" + integer2);
// return Integer.sum(integer,integer2);
// });
// int sum = integerList.stream()
// .reduce(20, (integer, integer2) -> integer + integer2);
// int sum = integerList.stream()
// .reduce(20, Integer::sum);
System.out.println("最终结果为:------" + sum);
}
static class Student {
public Student(Integer id, String name, Integer age, Long score) {
this.id = id;
this.name = name;
this.age = age;
this.score = score;
}
private Integer id;
private String name;
private Integer age;
private Long score;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Long getScore() {
return score;
}
public void setScore(Long score) {
this.score = score;
}
}
private static List<Student> list;
static {
list = new ArrayList<>();
list.add(new Student(1, "小明", 18, 85L));
list.add(new Student(2, "张三", 19, 81L));
list.add(new Student(3, "李四", 20, 79L));
list.add(new Student(4, "王二", 15, 90L));
list.add(new Student(4, "王三", 18, 90L));
}
}