Java8 Stream相关

简介: Java8 Stream相关

Java8 Stream相关

文章转载于https://mp.weixin.qq.com/s/K40HSvLVGpLUrdEz0ZTzng

java8的stream相关的用法以及技术点,如下图所示

Stream.png

Stream概述

Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。

Stream操作类型

Stream的操作主要分为以下两种类型:

  1. 中间操作,每次返回一个新的流,可以有多个。
  2. 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。

Stream特性

Stream有几个特性:

  1. stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
  2. stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
  3. stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。

Stream的创建

Stream可以通过集合数组创建。

1.java.util.Collection.stream()方法创建

    /**
     * 1.java.util.Collection.stream()方法创建
     */
    public static void typeOne(){
        List<Integer> list = Arrays.asList(1,2,3,4,5,6);
        //创建一个顺序流
        Stream<Integer> stream = list.stream();
        //创建一个并行流
        Stream<Integer> integerStream = list.parallelStream();
    }

2.java.util.Arrays.stream(T[] array)方法用数组创建流

   /**
     * 2.java.util.Arrays.stream(T[] array)方法用数组创建流
     */
    public static void typeTwo(){
        int [] array = {1,2,3,4,5,6};
        IntStream stream = Arrays.stream(array);
    }

3.使用Stream的静态方法:of()、iterate()、generate()

   /**
     *3.使用Stream的静态方法:of()、iterate()、generate()
     */
    public static void typeThree(){
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
        Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
        Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
    }

顺序流和并行流的区别

stream是顺序流,由主线程按顺序对流执行操作

parallelStream是并行流,内部以多线程并行执行的方式对流进行操作

在处理筛选集合中的奇数,两者的处理流程不同,操作过程示例如下:
顺序流与并行流.png

如果流中数据量很大,并行流可以加快处理速度。

除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流

Stream<Integer> parallel = list.stream().parallel();

Stream流的使用

1.遍历/匹配

Stream也是支持类似集合的遍历和匹配元素的,只是Stream中的元素是以Optional类型存在的。

遍历.png

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1, 5, 7, 6, 9, 4, 3, 1);
    //遍历输出元素
    System.out.println("遍历输出:");
    list.stream().forEach(System.out::println);
    //匹配第一个
    Optional<Integer> first = list.stream().findFirst();
    System.out.println("匹配第一个值:"+first.get());
    //匹配任意一个值(使用与并行流)
    Optional<Integer> any = list.stream().parallel().findAny();
    System.out.println("匹配任意一个值:"+any.get());
    boolean b = list.stream().anyMatch(x -> x > 5);
    System.out.println("是否存在大于5的值:"+b);
}

2.筛选

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

筛选.png

筛选集合中的元素并打印

//1.打印集合中大于7的元素
List<Integer> list = Arrays.asList(1, 5, 7, 6, 9, 4, 3, 1);
list.stream().filter(s->s>7).forEach(System.out::println);

筛选集合中的元素组成新的集合

/**
 * 初始化list数组
 */
public static void initEmployee() {
    employees.add(Employee.builder().name("Tom").age(25).salary(6000).address("New York").sex(0).build());
    employees.add(Employee.builder().name("Jack").age(28).salary(10000).address("New York").sex(0).build());
employees.add(Employee.builder().name("Lily").age(30).salary(7000).address("Washington").sex(1).build());
    employees.add(Employee.builder().name("Anni").age(32).salary(15000).address("New York").sex(1).build());
    employees.add(Employee.builder().name("Owen").age(22).salary(6500).address("Washington").sex(0).build());
    employees.add(Employee.builder().name("Alisa").age(21).salary(8000).address("New York").sex(1).build());
}

//2.过滤集合中工资高于10000的员工
        List<Employee> collect = employees.stream().filter(s -> s.getSalary() >= 10000).collect(Collectors.toList());
        //打印满足条件的员工的姓名
        collect.forEach(s-> System.out.println(s.getName()));

3.聚合(max/min/count)

maxmincount通常用于数据统计

聚合.png

获取string集合中最长的元素

//1.获取字符串中字符最长的字符串
List<String> list = Arrays.asList("Tom","Jack","Lily","Anni","Owen","jamsibul");
Optional<String> max = list.stream().max(Comparator.comparing(String::length));
System.out.println("最长的字符串:"+max.get());

获取Integer集合中的最大值

//2.获取集合中最小的数字
List<Integer> listInt = Arrays.asList(1,8,6,7,4,5,9,1,0,9);
Optional<Integer> min = listInt.stream().min(Integer::compareTo);
System.out.println("最小的数字:"+min.get());

获取员工工资最高的人

//3.获取员工工资最高的人
Optional<Employee> maxEmployee = employees.stream().max(Comparator.comparing(Employee::getSalary));
System.out.println("员工工资最高的人:"+maxEmployee.get().getName());

获取Integer集合中大于6的元素个数

//4.获取集合中大于5的元素个数
long count = listInt.stream().filter(s -> s > 5).count();
System.out.println("获取集合中大于5的元素个数:"+count);

4.映射

映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为mapflatMap

  • map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  • flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

3.映射.png

英文字符串数组的元素全部改为大写。整数数组每个元素+3

 //1.英文字符串数组的元素全部改为大写
        String [] str = {"abc","afc","erewfs","gd"};
        List<String> collect = Arrays.stream(str).map(String::toUpperCase).collect(Collectors.toList());
        System.out.println("每个元素大写:"+collect);
        //2.整数数组每个元素+3
        List<Integer> intList = Arrays.asList(1,5,6,7,4,9,5);
        List<Integer> collect1 = intList.stream().map(s -> s + 3).collect(Collectors.toList());
        System.out.println("每个元素加3:"+collect1);
        System.out.println("调整前工资:"+employees.get(0).getName()+":"+employees.get(0).getSalary());

将员工的薪资全部增加1000

//3.将员工的薪资全部增加1000
List<Employee> collect2 = employees.stream().map(s -> {
    s.setSalary(s.getSalary() + 1000);
    return s;
}).collect(Collectors.toList());
System.out.println("每个员工工资加1000:"+collect2.get(0).getName()+":"+collect2.get(0).getSalary());

将两个字符数组合并成一个新的字符数组

//4.将两个字符数组合并成一个新的字符数组
List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
List<String> listNew = list.stream().flatMap(s -> {
    // 将每个元素转换成一个stream
    String[] split = s.split(",");
    Stream<String> s2 = Arrays.stream(split);
    return s2;
}).collect(Collectors.toList());
System.out.println("处理前的集合:" + list);
System.out.println("处理后的集合:" + listNew);

5.归约(reduce)

归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。

4.规约.png

求Integer集合的元素之和、乘积和最大值

public static void testReduce(){
    List<Integer> list = Arrays.asList(1, 3, 4, 6, 7, 9, 4, 10);
    //1.求和
    Optional<Integer> sum = list.stream().reduce(Integer::sum);
    System.out.println("求和:"+sum.get());
    //2.求乘积
    Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
    System.out.println("求乘积:"+product.get());
    //3.求最大值
    Optional<Integer> max = list.stream().reduce(Integer::max);
    System.out.println("求最大值:"+max.get());
}

求所有员工的工资之和和最高工资

//4.求所有员工的工资之和和最高工资
Optional<Integer> countSalary = employees.stream().map(Employee::getSalary).reduce(Integer::sum);
System.out.println("所有员工的工资之和:"+countSalary.get());
Integer reduce = employees.stream().reduce(0, (max, e) -> max > e.getSalary() ? max : e.getSalary(), Integer::max);
System.out.println("员工最高工资:"+reduce);

6.收集

collect,收集,把一个流收集起来,最终收集成一个值或者一个新的集合。

collect主要依赖java.util.stream.Collectors类内置的静态方法。

6.1归集

因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toListtoSettoMap比较常用,另外还有toCollectiontoConcurrentMap等复杂一些的用法。

public class SimpleDemo05 {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 5, 6, 7, 8, 9, 4);
        List<Integer> toList = list.stream().filter(s -> s > 5).collect(Collectors.toList());
        Set<Integer> toSet = list.stream().filter(s -> s > 5).collect(Collectors.toSet());
        List<Student> studentList = Arrays.asList(Student.builder().age(10).name("00").build(), Student.builder().age(11).name("11").build());
        Map<String, Student> toMap = studentList.stream().collect(Collectors.toMap(Student::getName, s -> s));
        System.out.println(toList);
        System.out.println(toSet);
        System.out.println(toMap);
    }
}
6.2统计

Collectors提供了一系列用于数据统计的静态方法:

  • 计数:count
  • 平均值:averagingIntaveragingLongaveragingDouble
  • 最值:maxByminBy
  • 求和:summingIntsummingLongsummingDouble
  • 统计以上所有:summarizingIntsummarizingLongsummarizingDouble
/**
 * 统计
 * @author likai
 * @Date 2022/3/31 14:38
 */
public class SimpleDemo06 {

    public static void main(String[] args) {
        List<Student> studentList = Arrays.asList(Student.builder().age(10).name("00").build(), Student.builder().age(11).name("11").build());
        // 求总数
        Long count = studentList.stream().collect(Collectors.counting());
        // 求平均
        Double average = studentList.stream().collect(Collectors.averagingDouble(Student::getAge));
        // 求最高
        Optional<Integer> max = studentList.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare));
        // 求之和
        Integer sum = studentList.stream().collect(Collectors.summingInt(Student::getAge));
        // 一次性统计所有信息
        DoubleSummaryStatistics collect = studentList.stream().collect(Collectors.summarizingDouble(Student::getAge));
        System.out.println("总数:" + count);
        System.out.println("平均:" + average);
        System.out.println("总和:" + sum);
        System.out.println("所有统计:" + collect);
    }
}
6.3分组

stream按条件分为多个Map

/**
 * 分组
 * @author likai
 * @Date 2022/3/31 14:43
 */
public class SimpleDemo07 {
    public static void main(String[] args) {
        List<Student> studentList = Arrays.asList(
                Student.builder().age(10).name("00").sex(0).address("hubei").build(),
                Student.builder().age(11).name("11").sex(0).address("hubei").build(),
                Student.builder().age(22).name("22").sex(1).address("hunan").build(),
                Student.builder().age(33).name("33").sex(1).address("hunan").build()
                );
        // 按年龄是否大于20分组
        Map<Boolean, List<Student>> part = studentList.stream().collect(Collectors.partitioningBy(x -> x.getAge() > 20));
        // 将性别分组
        Map<Integer, List<Student>> group = studentList.stream().collect(Collectors.groupingBy(Student::getSex));
        // 先按性别分组,再按地区分组
        Map<Integer, Map<String, List<Student>>> group2 = studentList.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.groupingBy(Student::getAddress)));
        System.out.println("按年龄是否大于20分组:" + part);
        System.out.println("将性别分组:" + group);
        System.out.println("先按性别分组,再按地区分组:" + group2);
    }
}
6.4接合

joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。

/**
 * 接合
 * @author likai
 * @Date 2022/3/31 14:51
 */
public class SimpleDemo08 {

    public static void main(String[] args) {
        List<Student> studentList = Arrays.asList(
                Student.builder().age(10).name("00").sex(0).address("hubei").build(),
                Student.builder().age(11).name("11").sex(0).address("hubei").build(),
                Student.builder().age(22).name("22").sex(1).address("hunan").build(),
                Student.builder().age(33).name("33").sex(1).address("hunan").build()
        );
        String name = studentList.stream().map(s -> s.getName()).collect(Collectors.joining("-"));
        System.out.println(name);
    }
}
6.5规约

Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持。

/**
 * 规约
 * @author likai
 * @Date 2022/3/31 16:19
 */
public class SimpleDemo09 {
    public static void main(String[] args) {
        List<Student> studentList = Arrays.asList(
                Student.builder().age(10).name("00").sex(0).address("hubei").build(),
                Student.builder().age(11).name("11").sex(0).address("hubei").build(),
                Student.builder().age(22).name("22").sex(1).address("hunan").build(),
                Student.builder().age(33).name("33").sex(1).address("hunan").build()
        );
        // Collectors的reduce
        Integer sum = studentList.stream().collect(Collectors.reducing(10, Student::getAge, (i, j) -> (i + j)));
        System.out.println("年龄总和:" + sum);
        // stream的reduce
        Optional<Integer> sum2 = studentList.stream().map(Student::getAge).reduce(Integer::sum);
        System.out.println("年龄总和:" + sum2.get());
    }
}
6.6排序

sorted,中间操作。有两种排序:

  • sorted():自然排序,流中元素需实现Comparable接口
  • sorted(Comparator com):Comparator排序器自定义排序
/**
 * 排序
 * @author likai
 * @Date 2022/3/31 16:24
 */
public class SimpleDemo10 {
    public static void main(String[] args) {
        List<Student> studentList = DemoStudentList.getStudentList();
        // 按年龄升序排序(自然排序)
        List<String> newList = studentList.stream().sorted(Comparator.comparing(Student::getAge)).map(Student::getName)
                .collect(Collectors.toList());
        // 按年龄倒序排序
        List<String> newList2 = studentList.stream().sorted(Comparator.comparing(Student::getAge).reversed())
                .map(Student::getName).collect(Collectors.toList());
        // 先按年龄再按身高升序排序
        List<String> newList3 = studentList.stream()
                .sorted(Comparator.comparing(Student::getAge).thenComparing(Student::getHeight)).map(Student::getName)
                .collect(Collectors.toList());
        // 先按年龄再按身高升序排序
        List<String> newList4 = studentList.stream().sorted((p1, p2) -> {
            if (p1.getAge() == p2.getAge()) {
                return p2.getAge() - p1.getAge();
            } else {
                return p2.getHeight() - p1.getHeight();
            }
        }).map(Student::getName).collect(Collectors.toList());

        System.out.println("按年龄升序排序:" + newList);
        System.out.println("按年龄倒序排序:" + newList2);
        System.out.println("先按年龄再按身高升序排序:" + newList3);
        System.out.println("先按年龄再按身高升序排序:" + newList4);
    }
}
6.7提取/组合

流也可以进行合并、去重、限制、跳过等操作。

/**
 * 提取/组合
 * @author likai
 * @Date 2022/3/31 17:16
 */
public class SimpleDemo11 {
    public static void main(String[] args) {
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };
        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);
        // concat:合并两个流 distinct:去重
        List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        // limit:限制从流中获得前n个数据
        List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip:跳过前n个数据
        List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
        System.out.println("流合并:" + newList);
        System.out.println("limit:" + collect);
        System.out.println("skip:" + collect2);
    }
}
相关文章
|
3月前
|
安全 Java API
告别繁琐编码,拥抱Java 8新特性:Stream API与Optional类助你高效编程,成就卓越开发者!
【8月更文挑战第29天】Java 8为开发者引入了多项新特性,其中Stream API和Optional类尤其值得关注。Stream API对集合操作进行了高级抽象,支持声明式的数据处理,避免了显式循环代码的编写;而Optional类则作为非空值的容器,有效减少了空指针异常的风险。通过几个实战示例,我们展示了如何利用Stream API进行过滤与转换操作,以及如何借助Optional类安全地处理可能为null的数据,从而使代码更加简洁和健壮。
106 0
|
14天前
|
Java API 数据处理
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。
|
1月前
|
Java 流计算
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
36 1
Flink-03 Flink Java 3分钟上手 Stream 给 Flink-02 DataStreamSource Socket写一个测试的工具!
|
1月前
|
Java Shell 流计算
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
22 1
Flink-02 Flink Java 3分钟上手 Stream SingleOutputStreamOpe ExecutionEnvironment DataSet FlatMapFunction
|
2月前
|
存储 Java API
Java——Stream流详解
Stream流是JDK 8引入的概念,用于高效处理集合或数组数据。其API支持声明式编程,操作分为中间操作和终端操作。中间操作包括过滤、映射、排序等,可链式调用;终端操作则完成数据处理,如遍历、收集等。Stream流简化了集合与数组的操作,提升了代码的简洁性
71 11
Java——Stream流详解
|
1月前
|
存储 Java 数据处理
Flink-01 介绍Flink Java 3分钟上手 HelloWorld 和 Stream ExecutionEnvironment DataSet FlatMapFunction
Flink-01 介绍Flink Java 3分钟上手 HelloWorld 和 Stream ExecutionEnvironment DataSet FlatMapFunction
29 1
|
2月前
|
Java API C++
Java 8 Stream Api 中的 peek 操作
本文介绍了Java中`Stream`的`peek`操作,该操作通过`Consumer&lt;T&gt;`函数消费流中的每个元素,但不改变元素类型。文章详细解释了`Consumer&lt;T&gt;`接口及其使用场景,并通过示例代码展示了`peek`操作的应用。此外,还对比了`peek`与`map`的区别,帮助读者更好地理解这两种操作的不同用途。作者为码农小胖哥,原文发布于稀土掘金。
110 9
Java 8 Stream Api 中的 peek 操作
|
2月前
|
Java C# Swift
Java Stream中peek和map不为人知的秘密
本文通过一个Java Stream中的示例,探讨了`peek`方法在流式处理中的应用及其潜在问题。首先介绍了`peek`的基本定义与使用,并通过代码展示了其如何在流中对每个元素进行操作而不返回结果。接着讨论了`peek`作为中间操作的懒执行特性,强调了如果没有终端操作则不会执行的问题。文章指出,在某些情况下使用`peek`可能比`map`更简洁,但也需注意其懒执行带来的影响。
130 2
Java Stream中peek和map不为人知的秘密
|
2月前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
|
2月前
|
Java 程序员 API
Java 8新特性之Lambda表达式与Stream API的探索
【9月更文挑战第24天】本文将深入浅出地介绍Java 8中的重要新特性——Lambda表达式和Stream API,通过实例解析其语法、用法及背后的设计哲学。我们将一探究竟,看看这些新特性如何让Java代码变得更加简洁、易读且富有表现力,同时提升程序的性能和开发效率。