Java 8 Stream 知识点详细讲解

Java 8 Stream 知识点详细讲解

什么是Stream

Stream 是 java 8 的新特性,Stream 是对集合功能的增强,它不是集合元素也不是数据结构,更不能用于保存数据,它是有关于算法和计算的。

Stream 将要处理的元素集合看作一种流,在流的过程中,借助 Stream API 对流中的元素进行操作,如查询、筛选、删除、过滤、统计、映射等。

也就是说 Stream 本身不负责存储数据,存储数据是用集合,数组等数据结构,它只负责对数据进行处理、加工。

Stream 的操作流程

  1. 创建 Stream :Stream 数据的来源
  2. 加工处理:可以是查询、筛选、删除、过滤等操作的一种或多种结合
  3. 终结操作:收集结果,一旦终结就不能再加工了,如果要加工需要重新创建Stream。

Stream 的创建

  1. 使用 Arrays.stream() 传入参数创建 Stream
Stream<String> stream1 = Arrays.stream(new String[]{
    
    "A", "B", "C", "D"});
stream1.forEach(System.out::println);

执行结果:

A
B
C
D
  1. 使用 Collection.stream() 方法返回 Stream
List<String> list = Arrays.asList("A", "B", "C", "D");
Stream<String> stream2 = list.stream();
Stream<String> parallelStream = list.parallelStream();
stream2.forEach(System.out::println);

执行结果:

A
B
C
D

注:stream 和 parallelStream 的区别:

  • stream 是顺序流,由主线程按顺序对流执行操作
  • parallelStream 里面的执行是异步的,并且使用的线程池是ForkJoinPool.common,可以通过设置Djava.util.concurrent.ForkJoinPool.common.parallelism = N来调整线程池的大小
  1. 使用 Stream 类下的静态方法 of()、iterate()、generate() 创建 Stream
// of()
Stream<String> stream3 = Stream.of("A", "B", "C", "D");
stream3.forEach(System.out::println);

// iterate()
Stream<String> stream4 = Stream.iterate("A", temp -> temp + "B").limit(4);
stream4.forEach(System.out::println);

// generate()
Stream<UUID> stream5 = Stream.generate(UUID::randomUUID).limit(4);
stream5.forEach(System.out::println);

执行结果:

A
B
C
D
------------------------------------
A
AB
ABB
ABBB
------------------------------------
df4c1baa-765c-4aea-9052-f1033fa821bd
93630562-3f62-45f6-9fca-828ffeb76272
51bf8b72-785b-4c07-8e7a-b200842cb588
d57b97c0-f6ec-4a76-bc82-6c6e8c8654f1

Stream 的详细使用

在详细使用 Stream 之前需要先知道什么是 Optional 类。Java8 新增了非常多的特性,而 Optional 类就是其中一个新增的类。

  • Optional 类是一个可以为 null 的容器对象。
  • 如果值存在则 isPresent() 方法会返回 true,调用 get() 方法会返回该对象。
  • Optional 容器可以保存类型 T 的值,或者仅仅保存 null。
  • Optional 提供很多有用的方法,这样我们就不用显式进行空值检测。
  • Optional 类的引入很好的解决空指针异常。
    在这里插入图片描述

操作需要的 Student 类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    
    
    private String name;
    private Integer age;
    private String hobby;
    private Integer score;

    public static List<Student> getStudents() {
    
    
        List<Student> list = new ArrayList<>();
        list.add(new Student("张三", 24, "basketball", 95));
        list.add(new Student("李四", 15, "basketball", 78));
        list.add(new Student("王五", 30, "basketball", 80));
        list.add(new Student("赵六", 18, "basketball", 85));
        list.add(new Student("李刚", 22, "basketball", 68));
        list.add(new Student("小明", 16, "basketball", 100));
        return list;
    }
}

注:还没有用过 Lombok 的同学一定要了解一下哦。

find 和 match(查找和匹配)

// find 找到匹配规则的对象
// match 是否存在匹配规则对象
public class TestStreamFind {
    
    
    public static void main(String[] args) {
    
    

        List<Student> students = Student.getStudents();
        // findFirst() 找到符合标准的第一个
        Optional<Student> first = students.stream().filter(stu -> stu.getAge() > 22).findFirst();
        System.out.println("findFist() 找到第一个匹配对象:" + first.get());

        // findAny() 找到符合标准的随机一个,一般用于并行流中
        Optional<Student> any = students.stream().filter(stu -> stu.getScore() > 90).findAny();
        System.out.println("-------------------------------------");
        System.out.println("findAny() 找到随机一个匹配对象:" + any.get());

        // aynMatch() 是否存在匹配对象
        boolean result = students.stream().anyMatch(stu -> stu.getScore() < 50);
        System.out.println("-------------------------------------");
        System.out.println("anyMatch() 返回是否存在匹配对象:" + result);
    }
}

执行结果

findFist() 找到第一个匹配对象:Student(name=张三, age=24, hobby=basketball, score=88)
-------------------------------------
findAny() 找到随机一个匹配对象:Student(name=赵六, age=18, hobby=basketball, score=95)
-------------------------------------
anyMatch() 返回是否存在匹配对象:false

filter 和 forEach(筛选和遍历)

// filter 筛选符合规则的对象
// forEach 遍历流中的对象
// collect 收集流中的内容
public class TestStreamFilter {
    
    
    public static void main(String[] args) {
    
    
        List<Student> list = Student.getStudents();
        list.stream().filter(stu -> stu.getAge() > 20).forEach(System.out::println);

        // 将 stream() 收集成集合
        List<Student> newList = list.stream().filter(stu -> stu.getAge() > 20).collect(Collectors.toList());
        System.out.println("--------------------------------");
        System.out.println(newList);
    }
}

执行结果

Student(name=张三, age=24, hobby=basketball, score=88)
Student(name=王五, age=30, hobby=basketball, score=80)
Student(name=李刚, age=22, hobby=basketball, score=68)
--------------------------------
[Student(name=张三, age=24, hobby=basketball, score=88), Student(name=王五, age=30, hobby=basketball, score=80), Student(name=李刚, age=22, hobby=basketball, score=68)]

max、min 和 count(最大值,最小值和计数)

// max 找到符合规则的最大值
// min 找到符合规则的最大值
// count 找到符合规则的个数
public class TestStreamMinMaxCount {
    
    
    public static void main(String[] args) {
    
    
        List<Student> list = Student.getStudents();

        Optional<Student> max = list.stream().max(Comparator.comparing(Student::getAge));
        System.out.println("max() 找到的年龄最大的对象:" + max.get());

        Optional<Student> min = list.stream().min(Comparator.comparing(Student::getScore));
        System.out.println("min() 找到的分数最小的对象:" + max.get());

        long count = list.stream().filter(stu -> stu.getScore() > 90).count();
        System.out.println("count() 找到分数在90分以上的对象的个数:" + count);
    }
}

执行结果

max() 找到的年龄最大的对象:Student(name=王五, age=30, hobby=basketball, score=80)
min() 找到的分数最小的对象:Student(name=王五, age=30, hobby=basketball, score=80)
count() 找到分数在90分以上的对象的个数:2

map 和 flatMap(映射)

public class TestStreamMap {
    
    
    public static void main(String[] args) {
    
    
        // 获取年龄大于 23 的同学
        List<Student> list = Student.getStudents().stream().filter(stu -> stu.getAge() > 23).collect(Collectors.toList());
        // 遍历该 list
        list.forEach(System.out::println);

        System.out.println("----------------------所有同学年龄加1-------------------------");
        // 让该 list 中的所有同学年龄加 1
        // collect 将 stream 转成集合
        list.stream().map(stu -> {
    
    
            stu.setAge(stu.getAge() + 1);
            return stu;
        }).collect(Collectors.toList()).forEach(System.out::println);

        // flatMap 将两个 list 合一
        System.out.println("----------------------flatMap将两个list合一-------------------------");
        List<Student> list2 = Student.getStudents().stream().filter(stu -> stu.getAge() > 23).collect(Collectors.toList());
        List<List<Student>> nowList = Arrays.asList(list, list2);
        nowList.stream().flatMap(stus -> {
    
    
            Stream<Student> stream = stus.stream();
            return stream;
        }).forEach(System.out::println);
    }
}

执行结果

Student(name=张三, age=24, hobby=basketball, score=88)
Student(name=王五, age=30, hobby=basketball, score=80)
----------------------所有同学年龄加1-------------------------
Student(name=张三, age=25, hobby=basketball, score=88)
Student(name=王五, age=31, hobby=basketball, score=80)
----------------------flatMap将两个list合一-------------------------
Student(name=张三, age=25, hobby=basketball, score=88)
Student(name=王五, age=31, hobby=basketball, score=80)
Student(name=张三, age=24, hobby=basketball, score=88)
Student(name=王五, age=30, hobby=basketball, score=80)

reduce(累加器)

// reduce 其实它不是累加,应该说是累计计算,先把前两个根据规则算出结果,可以是累加,累乘,比大小等等
// 再和第 3 个算出结果,依此类推,直到所有元素运算完毕。
// 如果传入第 1 个参数,则先将该参数与第一个元素结合运算
public class TestStreamReduce {
    
    
    public static void main(String[] args) {
    
    
        // 获取累加的 3 种方式
        List<Integer> list = Arrays.asList(1, 3, 5, 7, 9);
        // 方式 1
        Optional<Integer> sum1 = list.stream().reduce((x, y) -> x + y);
        // 方式 2
        Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
        // 方式 3
        Integer sum3 = list.stream().reduce(0, Integer::sum);
        System.out.println("reduce() 的求和结果为:" + sum3);


        // 分数求和
        List<Student> students = Student.getStudents();

        Optional<Integer> result1 = students.stream().map(Student::getScore).reduce(Integer::sum);
        Integer result2 = students.stream().reduce(0, (sum, stu) -> sum += stu.getScore(), (param1, param2) -> param1 + param2);
        Integer result3 = students.stream().reduce(0, (sum, stu) -> sum += stu.getScore(), Integer::sum);
        System.out.println("reduce() 的分数求和结果为:" + result3);


        // 分数最大值
        students.stream().reduce(0, (max, stu) -> max > stu.getScore() ? max : stu.getScore(), (param1, param2) -> param1 > param2 ? param1 : param2);
        Integer maxScore = students.stream().reduce(0, (max, stu) -> max > stu.getScore() ? max : stu.getScore(), Integer::max);
        System.out.println("reduce() 的求分数最大值结果为:" + maxScore);
    }
}

执行结果

reduce() 的求和结果为:25
reduce() 的分数求和结果为:509
reduce() 的求分数最大值结果为:100

collect(收集)

toList、toSet、toMap(转换)

public class TestStreamCollect {
    
    
    public static void main(String[] args) {
    
    
        // toList() 将分数大于 90 的学生的信息从流收集到 List 中
        Student.getStudents().stream().filter(stu -> stu.getScore() > 90).collect(Collectors.toList()).forEach(System.out::println);

        // toSet() 将 1,2,3,4,1 收集到 Set 中
        System.out.println("----------------------------------------------");
        Arrays.asList(1, 2, 3, 4, 1).stream().collect(Collectors.toSet()).forEach(System.out::println);

        // toMap() 将所有同学的 List 集合转成流,再收集成 k,v = name,Student 的 map
        System.out.println("----------------------------------------------");
        Student.getStudents().stream().collect(Collectors.toMap(Student::getName, stu -> stu)).entrySet().forEach(System.out::println);
    }
}

执行结果

Student(name=赵六, age=18, hobby=basketball, score=95)
Student(name=小明, age=16, hobby=basketball, score=100)
----------------------------------------------
1
2
3
4
----------------------------------------------
李刚=Student(name=李刚, age=22, hobby=basketball, score=68)
李四=Student(name=李四, age=15, hobby=basketball, score=78)
张三=Student(name=张三, age=24, hobby=basketball, score=88)
小明=Student(name=小明, age=16, hobby=basketball, score=100)
王五=Student(name=王五, age=30, hobby=basketball, score=80)
赵六=Student(name=赵六, age=18, hobby=basketball, score=95)

joining(拼接)

// join 拼接字符串
public class TestStreamJoining {
    
    
    public static void main(String[] args) {
    
    
        // 将所有同学的名字用 , 隔开
        System.out.println(Student.getStudents().stream().map(Student::getName).collect(Collectors.joining(",")));
        // 将所有同学的名字用 , 隔开,并用 [ 当前缀,用 ] 当后缀
        System.out.println(Student.getStudents().stream().map(Student::getName).collect(Collectors.joining(",","[","]")));
    }
}

执行结果

张三,李四,王五,赵六,李刚,小明
[张三,李四,王五,赵六,李刚,小明]

groupingBy 和 partitioningBy(分组)

// groupingBy 根据条件进行分组
public class TestStreamGroupingBy {
    
    
    public static void main(String[] args) {
    
    
        // 通过 hobby 对 List 进行分组
        Student.getStudents().stream().collect(Collectors.groupingBy(Student::getHobby)).entrySet().forEach(System.out::println);
        
        // 通过 age 是否大于 20 对 List 进行分组
        System.out.println("-----------------------------------------");
        Student.getStudents().stream().collect(Collectors.partitioningBy(stu -> stu.getAge() > 20)).entrySet().forEach(System.out::println);
    }
}

执行结果

soccer=[Student(name=王五, age=30, hobby=soccer, score=80), Student(name=赵六, age=18, hobby=soccer, score=95)]
basketball=[Student(name=张三, age=24, hobby=basketball, score=88), Student(name=李刚, age=22, hobby=basketball, score=68)]
run=[Student(name=小明, age=16, hobby=run, score=100)]
baseball=[Student(name=李四, age=15, hobby=baseball, score=78)]
-----------------------------------------
false=[Student(name=李四, age=15, hobby=baseball, score=78), Student(name=赵六, age=18, hobby=soccer, score=95), Student(name=小明, age=16, hobby=run, score=100)]
true=[Student(name=张三, age=24, hobby=basketball, score=88), Student(name=王五, age=30, hobby=soccer, score=80), Student(name=李刚, age=22, hobby=basketball, score=68)]

counting 和 averagingLong(统计)

Collectors 类中有一系列常用的统计方法

  • 计数:count
  • 平均值:averagingInt、averagingLong、averagingDouble
  • 最值:maxBy、minBy
  • 求和:summingInt、summingLong、summingDouble
  • 统计所有:summarizingInt、summarizingLong、summarizingDouble
public class TestStreamCountAvrage {
    
    
    public static void main(String[] args) {
    
    

        // 计算学生的总数
        Long count = Student.getStudents().stream().collect(Collectors.counting());
        System.out.println("counting() 方法计算的总数为:" + count);

        // 计算学生平均分数
        Double averageScore = Student.getStudents().stream().collect(Collectors.averagingLong(Student::getScore));
        System.out.println("averagingLong() 方法计算的平均分为:" + averageScore);

        // 计算年龄最大的学生
        Optional<Integer> maxAge = Student.getStudents().stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compareTo));
        System.out.println("maxBy() 方法计算的最大年龄为:" + maxAge.get());

        // 计算所有同学的分数和
        Integer sumScore = Student.getStudents().stream().collect(Collectors.summingInt(Student::getScore));
        System.out.println("summingInt() 方法计算所有学生分数和为:" + sumScore);

        // 统计同学关于分数的所有信息
        DoubleSummaryStatistics collect = Student.getStudents().stream().collect(Collectors.summarizingDouble(Student::getScore));
        System.out.println("summarizingDouble() 统计关于分数的信息:" + collect);
    }
}

执行结果

counting() 方法计算的总数为:6
averagingLong() 方法计算的平均分为:84.83333333333333
maxBy() 方法计算的最大年龄为:30
summingInt() 方法计算所有学生分数和为:509
summarizingDouble() 统计关于分数的信息:DoubleSummaryStatistics{count=6, sum=509.000000, min=68.000000, average=84.833333, max=100.000000}

reducing(累加)

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

// reducing
public class TestStreamReducing {
    
    
    public static void main(String[] args) {
    
    

        // 分数错误每人多了 10 分,求所有同学正确的分数和
        Integer rightScore = Student.getStudents().stream().collect(Collectors.reducing(0, Student::getScore,
                (param1, param2) -> param1 + param2 - 10));
        System.out.println("reducing() 方法计算所有同学分数减10分的总分数:" + rightScore);
    }
}

执行结果

reducing() 方法计算所有同学分数减10分的总分数:449

sorted(排序)

public class TestStreamSort {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("按分数升序排序:");
        Student.getStudents().stream().sorted(Comparator.comparing(Student::getScore)).forEach(System.out::println);
        System.out.println("-----------------------------------------------------------");

        System.out.println("按分数降序排序:");
        Student.getStudents().stream().sorted(Comparator.comparing(Student::getScore).reversed()).forEach(System.out::println);
        System.out.println("-----------------------------------------------------------");

        System.out.println("先按分数再按年龄年龄排序:");
        Student.getStudents().stream().sorted(Comparator.comparing(Student::getScore).thenComparing(Student::getAge)).forEach(System.out::println);
        System.out.println("-----------------------------------------------------------");

        System.out.println("先按分数再按年龄自定义降序排序:");
        Student.getStudents().stream().sorted((stu1, stu2) -> {
    
    
            if (stu1.getScore() == stu2.getScore())
                return stu2.getAge() - stu1.getAge();
            return stu1.getScore() - stu2.getScore();
        }).forEach(System.out::println);
    }
}

执行结果

按分数升序排序:
Student(name=李四, age=15, hobby=baseball, score=78)
Student(name=李刚, age=22, hobby=basketball, score=78)
Student(name=王五, age=30, hobby=soccer, score=80)
Student(name=赵六, age=18, hobby=soccer, score=80)
Student(name=张三, age=24, hobby=basketball, score=88)
Student(name=小明, age=16, hobby=run, score=100)
-----------------------------------------------------------
按分数降序排序:
Student(name=小明, age=16, hobby=run, score=100)
Student(name=张三, age=24, hobby=basketball, score=88)
Student(name=王五, age=30, hobby=soccer, score=80)
Student(name=赵六, age=18, hobby=soccer, score=80)
Student(name=李四, age=15, hobby=baseball, score=78)
Student(name=李刚, age=22, hobby=basketball, score=78)
-----------------------------------------------------------
先按分数再按年龄年龄排序:
Student(name=李四, age=15, hobby=baseball, score=78)
Student(name=李刚, age=22, hobby=basketball, score=78)
Student(name=赵六, age=18, hobby=soccer, score=80)
Student(name=王五, age=30, hobby=soccer, score=80)
Student(name=张三, age=24, hobby=basketball, score=88)
Student(name=小明, age=16, hobby=run, score=100)
-----------------------------------------------------------
先按分数再按年龄自定义降序排序:
Student(name=李刚, age=22, hobby=basketball, score=78)
Student(name=李四, age=15, hobby=baseball, score=78)
Student(name=王五, age=30, hobby=soccer, score=80)
Student(name=赵六, age=18, hobby=soccer, score=80)
Student(name=张三, age=24, hobby=basketball, score=88)
Student(name=小明, age=16, hobby=run, score=100)

concat、limit、skip(连接,限长,跳跃)

public class TestStreamConcatLimitSkipDistinct {
    
    
    public static void main(String[] args) {
    
    
        Stream<Student> stream1 = Student.getStudents().stream().filter(stu -> stu.getAge() > 23);
        Stream<Student> stream2 = Student.getStudents().stream().filter(stu -> stu.getScore() > 90);

        System.out.println("--------------使用 concat 连接流,使用 distinct 去重----------------");
        Stream.concat(stream1, stream2).distinct().forEach(System.out::println);

        System.out.println("---------------------使用 limit 限定只要前几个----------------------");
        Student.getStudents().stream().limit(3).forEach(System.out::println);

        System.out.println("---------------------使用 skip 跳过指定个数-----------------------");
        Student.getStudents().stream().skip(3).forEach(System.out::println);
    }
}

执行结果

--------------使用 concat 连接流,使用 distinct 去重----------------
Student(name=张三, age=24, hobby=basketball, score=88)
Student(name=王五, age=30, hobby=soccer, score=80)
Student(name=小明, age=25, hobby=run, score=100)
---------------------使用 limit 限定只要前几个----------------------
Student(name=张三, age=24, hobby=basketball, score=88)
Student(name=李四, age=15, hobby=baseball, score=78)
Student(name=王五, age=30, hobby=soccer, score=80)
---------------------使用 skip 跳过指定个数-----------------------
Student(name=赵六, age=18, hobby=soccer, score=80)
Student(name=李刚, age=22, hobby=basketball, score=78)
Student(name=小明, age=25, hobby=run, score=100)

猜你喜欢

转载自blog.csdn.net/qq_42647711/article/details/109766732
今日推荐