Java Stream流
1、概述
Java 8 API添加的一个新的抽象,称为流Stream,以一种声明性方式处理数据集合(侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式)
2、使用流程
- 创建流:将集合转换为Stream流
- 中间操作:过滤、映射等,返回一个操作后的流,可以有多次
- 终端操作:处理结果的获取等,只能有一次
3、接口继承关系
- BaseStream:基础接口,声明了流管理的核心方法;
- Stream:核心接口,声明了流操作的核心方法,其他接口为指定类型的适配
4、创建流
4.1、并行流和顺序流
- 顺序流:顺序流指的是一个任务依次执行,执行完一个任务之后再执行下一个任务
- 并行流:并行流是一种并行处理数据的方式。在并行流中,数据会被拆分成多个部分,并由不同的处理器核心并行处理。这样可以大大缩短处理大量数据的时间。
- 适用场景:顺序流适用于数据量较小的情况,处理速度较快。而并行流适用于大数据量的情况,可以充分利用计算机的多核CPU,提高数据处理速度。
- 线程安全:并行流和顺序流都是线程安全的。在多线程环境下,它们不会发生数据竞争等线程安全问题。
- 效率比较:在处理大量数据的情况下,相比顺序流,并行流可以获得较大的性能提升。但是在某些情况下,并行流的效率可能不如顺序流。
- 流只能有一个终止操作
4.2、Stream创建
Stream<Integer> stream = Stream.of(1,2,3,4,5);//顺序流
Stream<Integer> parallelStream = stream.parallel();//顺序流转并行流
Stream<Integer> sequentialStream = parallelStream.sequential();//并行流转顺序流
4.3、Collection集合创建
List<Integer> integerList = new ArrayList<>();
integerList.add(1);
integerList.add(2);
integerList.add(3);
integerList.add(4);
integerList.add(5);
Stream<Integer> listStream = integerList.stream();//顺序流
Stream<Integer> listParallelStream = integerList.parallelStream();//并行流
4.4、Array数组创建
int[] intArray = { 10, 20, 30 };
IntStream intStream = Arrays.stream(intArray);// 顺序流
IntStream parallelIntStream = intStream.parallel();// 并行流
long[] longArray = { 10L, 20L, 30L };
LongStream longStream = Arrays.stream(longArray);// 顺序流
LongStream parallelLongStream = longStream.parallel();// 并行流
double[] doubleArray = { 10.1, 20.2, 30.3 };
DoubleStream doubleStream = Arrays.stream(doubleArray);// 顺序流
DoubleStream parallelDoubleStream = doubleStream.parallel();// 并行流
Stream<Integer> boxed = intStream.boxed();//数值流转对象流
IntStream intStream1 = boxed.mapToInt(s->s.intValue());//对象流转数值流
注意:
-
通过Arrays.stream方法生成流,并且该方法生成的流是数值流【即IntStream、LongStream、DoubleStream】而不是 Stream
-
使用数值流可以避免计算过程中拆箱装箱,提高性能。
-
Stream API提供了mapToInt、mapToDouble、mapToLong三种方式将对象流【即Stream 】转换成对应的数值流,同时提供了boxed方法将数值流转换为对象流
4.5、文件创建
try {
Stream<String> fileStream = Files.lines(Paths.get("data.txt"), Charset.defaultCharset());
} catch (IOException e) {
e.printStackTrace();
}
通过Files.line方法得到一个流,并且得到的每个流是给定文件中的一行
4.6、函数创建无限流
-
iterator
Stream<Integer> iterateStream = Stream.iterate(0, n -> n + 2).limit(5);iterate方法接受两个参数,第一个为初始化值,第二个为进行的函数操作,因为iterator生成的流为无限流,通过limit方法对流进行了截断,只生成5个偶数
-
generator
Stream<Double> generateStream = Stream.generate(Math::random).limit(5);generate方法接受一个参数,方法参数类型为Supplier ,由它为流提供值。generate生成的流也是无限流,因此通过limit对流进行了截断
5、中间操作
5.1、操作符介绍
| 操作符 | 描述 |
|---|---|
| filter | 用于通过设置的条件过滤出元素 |
map | 接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素 |
| mapToInt | 接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素,返回数值流 |
| mapToLong | 接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素,返回数值流 |
| mapToDouble | 接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素,返回数值流 |
| distinct | 返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流 |
| sorted | 返回排序后的流 |
| limit | 会返回一个不超过给定长度的流 |
| skip | 返回一个扔掉了前n个元素的流 |
| flatMap | 流扁平化,让你把一个流中的“每个值”都换成另一个流,然后把所有的流连接起来成为一个流 ,返回对象流 |
| flatMapToInt | 流扁平化,让你把一个流中的“每个值”都换成另一个流,然后把所有的流连接起来成为一个流,返回数值流 |
| flatMapToLong | 流扁平化,让你把一个流中的“每个值”都换成另一个流,然后把所有的流连接起来成为一个流 ,返回数值流 |
| flatMapToDouble | 流扁平化,让你把一个流中的“每个值”都换成另一个流,然后把所有的流连接起来成为一个流 ,返回数值流 |
| peek | 对元素进行遍历处理 ,常用于操作间结果输出 |
5.2、演示示例
5.2.1、filter代码演示
public class StramDemo1 {
public static void main(String[] args) {
// 创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
// 需求1:把list集合中以张开头的元素在控制台输出
list.stream().filter(s -> s.startsWith("张")).forEach(System.out::println);
System.out.println("----------");
// 需求2:把list集合中长度为3的元素在控制台输出
list.stream().filter(s -> s.length() == 3).forEach(System.out::println);
System.out.println("----------");
// 需求3:把list集合中以张开头的且长度为3的元素在控制台输出
list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(System.out::println);
System.out.println("----------");
}
}
5.2.2、limit&skip代码演示
public class StramDemo2 {
public static void main(String[] args) {
// 创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("张曼玉");
list.add("王祖贤");
list.add("柳岩");
list.add("张敏");
list.add("张无忌");
//limit&skip代码演示:
// 需求1:取前3个数据在控制台输出
list.stream().limit(3).forEach(System.out::println);
System.out.println("----------");
// 需求2:跳过3个元素,把剩下的元素在控制台输出
list.stream().skip(3).forEach(System.out::println);
System.out.println("----------");
// 需求3:跳过2个元素,把剩下的元素中前2个在控制台输出
list.stream().skip(2).limit(2).forEach(System.out::println);
}
}
5.2.3、distinct代码演示
public class StreamDemo3 {
public static void main(String[] args) {
// 创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("林青霞");
list.add("张曼玉");
list.add("张曼玉");
list.add("王祖贤");
list.add("王祖贤");
list.add("柳岩");
list.add("柳岩");
list.add("张敏");
list.add("张敏");
list.add("张无忌");
list.add("张无忌");
// 需求1:把集合元素在控制台输出,要求字符串元素不能重复
list.stream().distinct().forEach(System.out::println);
}
}
5.2.4、sorted代码演示
public class StreamDemo4 {
public static void main(String[] args) {
// 创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("linqingxia");
list.add("zhangmanyu");
list.add("wangzuxian");
list.add("liuyan");
list.add("zhangmin");
list.add("zhangwuji");
// 需求1:按照字母顺序把数据在控制台输出
list.stream().sorted().forEach(System.out::println);
System.out.println("----------");
// 需求2:按照字符串长度把数据在控制台输出
list.stream().sorted((s1, s2) -> {
int num1 = s1.length() - s2.length();
int num2 = num1 == 0 ? s1.compareTo(s2) : num1;
return num2;
}).forEach(System.out::println);
}
}
5.2.5、map代码演示
public class StreamDemo5 {
public static void main(String[] args) {
// 创建一个集合,存储多个字符串元素
ArrayList<String> list = new ArrayList<String>();
list.add("10");
list.add("20");
list.add("30");
list.add("40");
list.add("50");
// 需求1:将集合中的字符串数据转换为整数之后在控制台输出
list.stream().map(Integer::parseInt).forEach(System.out::println);
System.out.println("----------");
// 需求2:将集合中的字符串数据转换为Integer之后在控制台输出
list.stream().mapToInt(Integer::parseInt).forEach(System.out::println);
System.out.println("----------");
// 需求3:将集合中的字符串数据转换为Long之后在控制台输出
list.stream().mapToLong(Long::parseLong).forEach(System.out::println);
System.out.println("----------");
// 需求4:将集合中的字符串数据转换为Double之后在控制台输出
list.stream().mapToDouble(Double::parseDouble).forEach(System.out::println);
}
}
5.2.6、flatMap代码演示
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
collection.stream().flatMap(s -> Stream.of(s.split(""))).forEach(System.out::println);
5.2.7、peek代码演示
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
collection.stream()
.map(s -> s.toLowerCase())
.peek(s -> System.out.println("toLowerCase value: " + s))
.map(s -> s.toUpperCase())
.peek(s -> System.out.println("toUpperCase value: " + s))
.forEach(System.out::println);
6、终端操作
6.1、操作符介绍
Stream流执行完终端操作之后,无法再执行其他动作,否则会报状态异常,提示该流已经被执行操作或者被关闭,想要再次执行操作必须重新创建Stream流
一个流有且只能有一个终端操作,当这个操作执行后,流就被关闭了,无法再被操作,因此一个流只能被遍历一次,若想在遍历需要通过源数据在生成流。
| 操作符 | 没描述 |
|---|---|
| collect | 收集器,将流转换为其他形式 |
| forEach | 用于串行流保持顺序遍历 |
| forEachOrdered | 用于并行流保持顺序遍历 |
| findFirst | 返回第一个元素 |
| findAny | 将返回当前流中的任意元素 |
| count | 返回流中元素总数 |
| 数值流的sum、min、max、average | 数值流求和、最小值、最大值、平均数 |
| max | 最大值 |
| min | 最小值 |
| anyMatch | 检查是否至少匹配一个元素,返回boolean |
| allMatch | 检查是否匹配所有元素,返回boolean |
| noneMatch | 检查是否没有匹配所有元素,返回boolean |
| reduce | 可以将流中元素反复结合起来,得到一个值 |
6.2、演示示例
6.2.1、collect
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
ArrayList<String> collect1 = collection.parallelStream().collect(Collectors.toCollection(ArrayList<String>::new));
LinkedList<String> collect2 = collection.parallelStream().collect(Collectors.toCollection(LinkedList<String>::new));
List<String> collect3 = collection.parallelStream().collect(Collectors.toList());
Set<String> collect4 = collection.parallelStream().collect(Collectors.toSet());
Map<String, String> collect5 = collection.parallelStream().collect(Collectors.toMap(k -> k, v -> v, (v1, v2) -> v1));
String string = collection.parallelStream().collect(Collectors.joining());
6.2.2、forEach
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
collection.parallelStream().forEach(System.out::println);
6.2.3、forEachOrdered
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
collection.parallelStream().forEachOrdered(System.out::println);
6.2.4、findFirst
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
Optional<String> first = collection.parallelStream().findFirst();
if (first.isPresent()) {
System.out.println(first.get());
}
6.2.5、findAny
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
Optional<String> any = collection.parallelStream().findAny();
if (any.isPresent()) {
System.out.println(any.get());
}
6.2.6、count
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
long count = collection.parallelStream().count();
System.out.println(count);
6.2.7、数值流的sum、min、max、average
Collection<Integer> collection = Arrays.asList(3, 2, 1, 4, 5, 6);
//求和
int sum = collection.stream().mapToInt(i -> i.intValue()).sum();
System.out.println(sum);
//平均数
OptionalDouble average = collection.stream().mapToInt(i -> i.intValue()).average();
average.ifPresent(System.out::println);
//最小值
OptionalInt min = collection.stream().mapToInt(i -> i.intValue()).min();
min.ifPresent(System.out::println);
//最大值
OptionalInt max = collection.stream().mapToInt(i -> i.intValue()).max();
max.ifPresent(System.out::println);
6.2.8、max
Collection<Integer> collection = Arrays.asList(3, 2, 1, 4, 5, 6);
Optional<Integer> max = collection.stream().max(Comparator.comparingInt(o -> o));
max.ifPresent(System.out::println);
6.2.9、min
Collection<Integer> collection = Arrays.asList(3, 2, 1, 4, 5, 6);
Optional<Integer> max = collection.stream().min(Comparator.comparingInt(o -> o));
max.ifPresent(System.out::println);
6.2.10、anyMatch
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
boolean result = collection.parallelStream().anyMatch(s -> s.equals("aaa"));
System.out.println(result);
6.2.11、allMatch
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
boolean result = collection.parallelStream().allMatch(s -> s.equals("aaa"));
System.out.println(result);
6.2.12、noneMatch
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
boolean result = collection.parallelStream().noneMatch(s -> s.equals("aaa"));
System.out.println(result);
6.2.13、reduce
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
Optional<String> reduce = collection.parallelStream().reduce((acc, item) -> acc + item);
if (reduce.isPresent()) {
System.out.println(reduce.get());
}
String string = collection.parallelStream().reduce("prefix_", (acc, item) -> acc + item);
System.out.println(string);
7、collect收集
Collector:结果收集策略的核心接口,具备将指定元素累加存放到结果容器中的能力;并在Collectors工具中提供了Collector接口的实现类
7.1、toList
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
List<String> collect = collection.parallelStream().collect(Collectors.toList());
System.out.println(Arrays.toString(collect.toArray()));
//[abc, abd, abe, 123, 456, 789, aaa, bbb, ccc, aaa, abc]
7.2、toMap
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
//toMap第一个参数代表key,第二个参数代表value,第三个参数如果v1,v2相同时,选v1作为那个key对应的value值
Map<String, String> collect = collection.parallelStream().collect(Collectors.toMap(k -> k, v -> v, (v1, v2) -> v1));
7.3、toSet(会去重的)
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa");
Set<String> collect = collection.parallelStream().collect(Collectors.toSet());
System.out.println(Arrays.toString(collect.toArray()));
//[aaa, abd, 123, ccc, abc, bbb, abe, 456, 789]
7.4、counting(返回 个数)
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa","abc");
long count = collection.stream().filter(user -> user.startsWith("a")).collect(Collectors.counting());
System.out.println(count);//6
7.5、summingInt(求和)
Collection<Integer> collection = Arrays.asList(3, 2, 1, 4, 5, 6);
long sum = collection.stream().collect(Collectors.summingInt(v->v));
System.out.println(sum);//21
7.6、minBy(最小值)
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa","abc");
String s = collection.stream().collect(Collectors.minBy(String::compareTo)).get();
System.out.println(s);//123
7.7、joining(以指定分隔符链接成字符串)
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa","abc");
String s = collection.stream().collect(Collectors.joining(","));
System.out.println(s);//abc,abd,abe,123,456,789,aaa,bbb,ccc,aaa,abc
7.8、groupingBy(分组)
Collection<String> collection = Arrays.asList("abc", "abd", "abe", "123", "456", "789", "aaa", "bbb", "ccc", "aaa","abc");
Map<String, List<String>> collect = collection.stream().collect(Collectors.groupingBy(s -> s));
collect.forEach((k,list)->{
System.out.println(k+"="+Arrays.toString(list.toArray()));
});
/*
aaa=[aaa, aaa]
ccc=[ccc]
123=[123]
abd=[abd]
bbb=[bbb]
abc=[abc, abc]
456=[456]
abe=[abe]
789=[789]
*/