Java Stream流

271 阅读10分钟

Java Stream流

1、概述

Java 8 API添加的一个新的抽象,称为流Stream,以一种声明性方式处理数据集合(侧重对于源数据计算能力的封装,并且支持序列与并行两种操作方式)

2、使用流程

  • 创建流:将集合转换为Stream流
  • 中间操作:过滤、映射等,返回一个操作后的流,可以有多次
  • 终端操作:处理结果的获取等,只能有一次

3、接口继承关系

image-20230719145230625

image-20230719145316669

  • 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返回一个元素各异(根据流所生成元素的hashCodeequals方法实现)的流
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]
*/