分享

java8关于steam流的日常操作

 笑笑兔 2023-09-07 发布于天津
Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作。StreamAPI借助于同样出现的Lambda表达式,极大的提高编程效率和可读性。同时提供串行和并行两种模式操作。

1、Stream流的创建

通过 java.util.Collection.stream()方法用集合创建流。

List<String> list = Arrays.asList("hello","world","stream");
//创建顺序流
Stream<String> stream = list.stream();
//创建并行流
Stream<String> parallelStream = list.parallelStream();

使用java.util.Arrays.stream(T[] arr)方法用数组创建流。

 List<String> wordStr= Arrays.asList("word,test,dd,kkk,ffff".split(","));
  list = wordStr.stream().map(s -> {
            return s.toUpperCase().substring(0,1);
        }).collect(Collectors.toList());

2、无状态(Stateless)操作

2.1 filter:筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。

List<String> wordStr= Arrays.asList("word,test,dd,kkk,ffff".split(","));
//查找字符串长度大于3
List<String> filterStr = wordStr.stream().filter(s -> (s.length()>3)).collect(Collectors.toList());
        filterStr.forEach(s -> System.out.println(s));
//结果: word test ffff

2.2 映射(map、flatmap、peek)

  • map:将集合中的元素A转换成想要得到的B

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

代码示例:

    List<Person> personList = new ArrayList<>();
        personList.add(new Person("wjx",33,1));
        personList.add(new Person("li",23,2));
        List<String> name = personList.stream().map(Person::getName).collect(Collectors.toList());;
        System.out.println(name);
//结果:[wjx,li]

Person实体类,属性name、age 、id

  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。与Map功能类似,区别在于将结合A的流转换成B流

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

代码示例:

        String[] words = new String[]{"hello","world"};

        List<String> a1 = Arrays.stream(words)
                .map(word -> word.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(Collectors.toList());
        a1.forEach(System.out::print);
//打印结果:helowrd

  • peek:接收的是一个 Consumer<T>函数。顾名思义 peek 操作会按照Consumer<T>函数提供的逻辑去消费流中的每一个元素,同时有可能改变元素内部的一些属性。

Stream<T> peek(Consumer<? super T> action);

代码示例:

Stream<String> stream = Stream.of("hello", "world");
stream.peek(System.out::println);

执行之后,控制台并没有任何输出。流生命周期三个阶段:

* 起始生产阶段

* 中间操作会逐一获取元素并进行处理。可有可无。所有中间操作都是惰性的,因此,流在管道中流动之前,任何操作都不会产生任何影响。

* 终端操作。通常分为 最终的消费 (foreach 之类的)和 归纳(collect)两类。还有重要的一点就是终端操作启动了流在管道中的流动

以上代码改为:

Stream<String> stream = Stream.of("hello", "world");
stream.peek(System.out::println).collect(Collectors.toList());
 
//控制台打印内容如下:
hello
world

peek和map区别

peek :一般用于不会改变流中元素本身的类型或者只想元素的内部状态时;

map:则用于改变流中元素本身类型,即从元素中派生出另一种类型的操作。

  • mapToInt、mapToLong、mapToDouble、flatMapToDouble、flatMapToInt、flatMapToLong(针对特定的数据类型进行映射处理

代码示例如下,其他类型执行测试:

Stream<String> stream = Stream.of("hello", "world");

stream.mapToInt(s->s.length()).forEach(System.out::println);

//输出结果

5

5

3、有状态(stateful)

  • distinct:去重操作

代码示例:

List<String> stringList =  Arrays.asList("1", "2", "2", "3", "4", "4", "5");
stringList.stream().distinct().forEach(System.out::println);
//1
//2
//3
//4
//5

  • sorted:返回由该流的元素组成的流,并根据自然顺序排序

代码示例:

        List<String> stringList =  Arrays.asList("56", "1", "2", "6", "4", "8", "7");
        stringList.stream().sorted().forEach(System.out::println);
//        1
//        2
//        4
//        56
//        6
//        7

  • limit:获取流中n个元素

代码示例:

        List<String> stringList =  Arrays.asList("1",  "2", "3", "4", "8", "7");
        stringList.stream().limit(3).forEach(System.out::println);
//        1
//        2
//        3

  • anyMatch:Stream中只要有一个元素符合传入的predicate,返回true

代码示例:

  List<String> stringList =  Arrays.asList("1",  "222", "3", "4", "8", "7");
        System.out.println(stringList.stream().anyMatch(s -> {return s.length()>2;}));
// true

  • allMatch:Stream 中全部元素符合传入的 predicate,返回 true;

  • noneMatch:Stream 中没有一个元素符合传入的 predicate,返回 true;

  • findFirst:用于返回满足条件的第一个元素(但是该元素是封装在Optional类中)

代码示例:

Stream<Integer> stream = Stream.of(1,4,7,19,8,5,9);
System.out.println("result="+stream.findFirst().get());
 
//输出
result=1
 
//当然,我们还可以结合filter处理
System.out.println("result="+stream.filter(s-> s > 3).findFirst().get());
 
//输出
result=4

  • findAny:返回流中的任意元素(该元素也是封装在Optional类中)

代码示例:

List<String> strAry = Arrays.asList( "lili", "hali", "Jack", "DaWei", "Jill","Dany");
 
String result = strAry.parallelStream().filter(s->s.startsWith("J")).findAny().get();
System.out.println("result = " + result);
 
//输出
result = Jill

findAny()操作,返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。如果是数据较少,串行情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个。

  • reduce:方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值

@reduce一个参数,主要作用 累加、累减,求取最大值、最小值

@Test
public void streamReduceOneTest() throws Exception{
    final ArrayList<Integer> list = Lists.newArrayList(1, 3, 5, 2, 4);
    // 累加, 1+3+5+2+4
    final Optional<Integer> reduce = list.stream().reduce((x, y) -> x + y);
    // 累减, 1-3-5-2-4
    final Optional<Integer> reduce2 = list.stream().reduce((x, y) -> x - y);
 
    System.out.println("reduce x+y ==>" + reduce.orElse(null));
    System.out.println("reduce2 x-y ==>" + reduce2.orElse(null));
 
    // BigDecimal 类型,累加求和
    final Optional<BigDecimal> reduce1 = list.stream().map(BigDecimal::new).reduce(BigDecimal::add);
    System.out.println("BigDecimal add : "+ reduce1.get());
 
    // 求取 最大值、最小值
    final Optional<Integer> reduce3 = list.stream().reduce(BinaryOperator.maxBy((x, y) -> x - y));
    final Optional<Integer> reduce4 = list.stream().reduce(BinaryOperator.maxBy((x, y) -> y - x));
    System.out.println("max : " + reduce3.get());
    System.out.println("min : " + reduce4.get());
}

@reduce两个参数,主要作用多了初始值:

@Test
public void streamReduceTwoTest() throws Exception {
    final ArrayList<Integer> list = Lists.newArrayList(1, 3, 5, 2, 4);
 
    // 2个参数,初始化10,一起累加 , 10+1+3+5+2+4
    final Integer reduce = list.stream().reduce(10, (x, y) -> x + y);
 
    // 2个参数,初始化10,一起累减 , 10-1-3-5-2-4
    final Integer reduce2 = list.stream().reduce(10, (x, y) -> x - y);
    System.out.println("初始化10,reduce x+y ==>" + reduce);
    System.out.println("初始化10,reduce2 x-y ==>" + reduce2);
 
    // 累乘, 10*1*3*5*2*4
    final Integer reduce1 = list.stream().reduce(10, (x, y) -> x * y);
    System.out.println("累积乘法 reduce1 ::: " + reduce1);
 
    // 最大值
    final Integer reduce3 = list.stream().reduce(10, BinaryOperator.maxBy((x, y) -> x - y));
    System.out.println("最大值: " +reduce3);
}

初始化10,reduce x+y ==>25
初始化10,reduce2 x-y ==>-5
累积乘法 reduce1 ::: 1200
最大值: 10

@reduce三个参数:

@Test
public void streamReduceThreeTest() throws Exception {
    final ArrayList<Integer> list = Lists.newArrayList(1, 3, 5, 2, 4);
    final Integer reduce = list.stream().reduce(20, (x, y) -> x + y, (t, r) -> t - r);
    System.out.println("reduce 3个参数:"+reduce);
 
    final ArrayList<String> strList = Lists.newArrayList("aa", "bb", "cc");
 
    // 字符串拼接处理:
    /**
     * (x, y) -> x.concat(";").concat(y):
     *  x:初始化 identity 参数 ,
     *  y: 集合中的每一个元素
     */
    final String reduce_three_arg = strList.stream().reduce(String.valueOf("reduce three arg: "), (x, y) -> x.concat(";").concat(y), (x, y) -> x);
    System.out.println("reduce 3个参数 22:"+reduce_three_arg);
 
    final String reduce_three_arg2 = strList.stream().reduce(String.valueOf(""), (x, y) -> x.concat(";").concat(y), (x, y) -> x);
    System.out.println("reduce 3个参数 2222:"+reduce_three_arg2);
}

输出结果:

reduce 3个参数:35
reduce 3个参数 22:reduce three arg: ;aa;bb;cc
reduce 3个参数 2222:;aa;bb;cc

@reduce三个参数并行执行

@Test
public void streamReduceThreeParallelTest() throws Exception {
    final List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
 
    final Integer reduce = list.stream().reduce(0, (x, y) -> x + y, (x, y) -> x * y);
    System.out.println(reduce);
 
    // 使用 并行流
    final Integer reduce2 = list.parallelStream().reduce(0, (x, y) -> x + y, (x, y) -> x * y);
    System.out.println(reduce2);
 
    final Integer reduce3 = list.stream().parallel().reduce(0, (x, y) -> x + y, (x, y) -> x * y);
    System.out.println(reduce3);
}

输出结果:

15
120
120
5

reduce总结:

1、reduce 方法:多个数据,合并为一个数据;主要功能如下:

  • 累积计算,加减乘除等运算,字符串拼接

  • 获取最值,最大值,最小值

 2、reduce三个参数区别

  • 一个参数:多个数据,合并为一个数据

  • 两个参数:比一个参数,多一个identity初始化数据

  • 三个参数:并行流的情况下:第三个参数有用,第二个参数没用非并行流反之 ...

Stream流总结

1、对于流的各种操作还不够熟悉的,可以直接进入方法的源码接口内,如下,是可以查看到类型说明的。

2、并行流stream().parallel()、parallelStream()的使用,须慎重使用!考虑多线程所带来的复杂性!

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多