⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 juejin.cn/post/7344026980616077350 「一只野生的八哥」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

Java 常见的Stream流式编程

  • Java 8 添加了一个新的抽象称为流 Stream API,可以让你以一种声明的方式处理数据。
  • Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
  • Stream API 可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

如何创建流?

集合创建:

  • stream() − 为集合创建串行流。在串行流中,每个元素都是由同一个线程处理的,因此操作是顺序执行的。
  • parallelStream() − 为集合创建并行流。在并行流中,流会将集合分成多个子集,然后多线程并行处理这些子集中的元素。
/**
* 集合创建流
*/
List<String> list = new ArrayList<>();
//串行流
Stream<String> stream = list.stream();
//并行流
Stream<String> stream = list.parallelStream();

数组创建:

通过数组创建的方式有:Stream.of()、Stream.generate()、Stream.iterate()、IntStream、LongStream 等,如下代码。

/**
* 数组创建流
*/
String[] strs = new String[3];
Stream<String> stream1 = Arrays.stream(strs);

/**
* Stream.of() 创建流
*/
Stream<String> stream2 = Stream.of("Apple", "Orange", "Banana");

/**
* Stream.generate()创建流
*/
Stream<Double> randomStream = Stream.generate(Math::random).limit(5);

/**
* Stream.iterate()创建流
*/
Stream<Integer> integerStream = Stream.iterate(0, n -> n + 2).limit(5);

/**
* IntStream创建流
*/
IntStream intStream = IntStream.range(1, 5); // [1,2,3,4]

/**
* LongStream创建流
*/
LongStream longStream = LongStream.rangeClosed(1, 5); // [1,2,3,4,5]

通常,我们遍历一个集合时会使用 for(int i = 0; i < list.size(); i++) 这种方式,但是当我们有 IntStream 可用时,可以优化为使用 IntStream.rangeClosed(0, list.size() - 1).forEach(i -> {...}) 来遍历集合。

中间操作

过滤操作(filter)

过滤操作,可以保留满足过滤条件的元素。如下代码为:将 lists 流中的等于 1 的元素过滤出来,并打印结果。

/**
* filter 过滤操作
* @param lists
*/
public static void filterTest(List<Integer> lists){
List<Integer> integers = lists.stream()
.filter(list ->Objects.equals(1, list)).toList();
integers.forEach(System.out::println);
}

映射操作(map / flatMap)

映射操作,可以对流中的每个元素提取并转换。如下代码为:

  • map 映射操作: 将 lists 流中的字符串,处理成大写的字符串;
  • flatMap 映射操作: 将 lists 流中的二位数组,转成一维数组。(flatMap 用于将流中的一个元素转换为一个流,然后将所有转换后的流组装成一个流)
/**
* map映射操作
*/
public static void mapTest(List<String> lists){
lists.stream().map(String::toLowerCase).toList();
lists.forEach(System.out::println);
}

/**
* flatMap映射操作,将嵌套集合转换为扁平集合
*/
public static void flatMapTest(List<List<String>> lists){
lists = Arrays.asList(
Arrays.asList("Java", "Kotlin"),
Arrays.asList("Python", "Ruby"),
Arrays.asList("JavaScript", "TypeScript")
);
List<String> wordList = lists.stream().flatMap(List::stream).toList();
wordList.forEach(System.out::println);
}

mapToInt / mapToLong / mapToDouble 操作

mapToInt / mapToLong / mapToDouble 操作,可以将流中的元素映射为对应 int / Long / Double 类型,并进一步做数值计算。

public class Student{
//年龄
private int age;
//姓名
private String Name;
}
public static void mapTest(){
int totalAge1 = students.stream().mapToInt(Student::getAge).sum();
Long totalAge2 = students.stream().mapToLong(Student::getAge).sum();
Double totalAge3 = students.stream().mapToDouble(Student::getAge).sum();
}

排序操作(sorted)

排序流中的元素。

/**
* 排序操作 sorted,对于集合对象排序,必须实现 Comparable接口,并重写 compareTo 方法,如果没有实现则需要给 sorted 函数传递参数
*/
public static void sortedTest(){
List<String> cities = Arrays.asList("New York", "Tokyo", "London", "Paris");
// 对城市按字母顺序排序
List<String> sortedStream = cities.stream().sorted(Comparator.comparingInt(city -> city.charAt(1))).toList();
sortedStream.forEach(System.out::println);
}

去重操作(distinct)

去除流中的重复元素。

/**
* 去重操作(distinct)
*/
public static void distinctTest(){
List<String> cities = Arrays.asList("New York", "Tokyo", "London", "Paris");
// 对城市去重
List<String> distinctStream = cities.stream().distinct();
sortedStream.forEach(System.out::println);
}

截断操作 / 跳过操作 / 观察操作(limit / skip / peek)

/**
* 截断操作(limit),只取流中前3个元素
*/
List<Integer> numbers = Lists.newArrayList(1, 2, 3, 4, 5, 6);
List<Integer> limitedList = numbers.stream().limit(3).collect(Collectors.toList());

/**
* 跳过操作(skip),跳过流中前3个元素
*/
List<Integer> numbers = Lists.newArrayList(1, 2, 3, 4, 5, 6);
List<Integer> limitedList = numbers.stream().skip(3).collect(Collectors.toList());

/**
* 观察操作,用于调试和观察流中的元素
*/
List<String> words = Arrays.asList("apple", "banana", "orange");
List<String> modifiedWords = words.stream()
.filter(word -> word.length() > 5)
.peek(word -> System.out.println("Filtered Word: " + word))
.map(String::toUpperCase)
.peek(word -> System.out.println("Uppercase Word: " + word))
.collect(Collectors.toList());

终端操作

迭代操作(forEach)

迭代操作,可以用于遍历流中的每一个元素。

List<String> fruits = Arrays.asList("apple", "banana", "orange");
fruits.stream().forEach(fruit -> System.out.println(fruit));
//fruits.forEach(fruit -> System.out.println(fruit));
//fruits.stream().forEach(System.out::println);

收集操作(collect)

收集操作,可以将流转换为 List、Set、Map 等集合。通过 collect 函数,对流中的元素进行收集。

// 将学生流转换为 List
List<Student> limitStu = students.stream().collect(Collectors.toList());

// 将学生流转换为 Set
List<Student> limitStu = students.stream().collect(Collectors.toSet());

// 将学生流转换为 Map,将年龄作为 key,将学生信息作为 value (Function.identity() 为提取元素自身的函数,(e1, e2)-> e1 代表当两个学生年龄相同时,取第一个学生)
Map<Integer, Student> studentMap = students.stream().collect(Collectors.toMap(
Student::getAge,
Function.identity(),
(e1,e2) -> e1));

//将学生的名字通过逗号拼接成一个字符串:studentName
String studentName = students.stream().map(Student::getName).collect(Collectors.joining(","));

//通过字符串的长度对 fruits 流分组
List<String> fruits = Arrays.asList("apple", "banana", "orange");
Map<Integer, List<String>> lengthToNamesMap = fruits.stream()
.collect(Collectors.groupingBy(String::length));

// counting(计算流元素个数)、maxBy(找到流中最大的元素)、minBy(找到流中最小的元素)、averagingInt(计算流中元素的平均值)、summarizingInt(计算流中元素的汇总统计信息,包括总数、平均值、最大值和最小值。)

partitioningBy(分组操作)

// 通过学生的年龄是否等于1进行分组,等于则分为 true 组,不等于则分为 false 组
Map<Boolean,List<Student>> partStudent = students.stream().collect(Collectors.partitioningBy(student -> Objects.equals(1,student.getAge())));

findFirst 操作

找到流中第一个元素。如下代码:找打学生流中第一个年龄等于1的元素。

Student firstStu = students.stream()  
.filter(student -> Objects.equals(1, student.getAge()))
.findFirst().orElse(null);

findAny 操作

找到流中任何一个元素。如下代码:找打学生流中任何一个年龄等于1的元素。(相当于 findFirst 操作)

Student student = students.stream()  
.filter(student -> Objects.equals(1, student.getAge()))
.findAny().orElse(null);

anyMatch 操作

检测是否存在一个或多个满足指定的参数行为,如果满足则返回true。

boolean result = students.stream()
.anyMatch(student -> Objects.equals(1, student.getAge()));

noneMatch 操作

检测是否不存在满足指定行为的元素,如果不存在则返回true。

boolean result = students.stream()
.noneMatch(student -> Objects.equals(1, student.getAge()));

allMatch 操作

检测是否全部都满足指定的参数行为,如果全部满足则返回true。

boolean result = students.stream()
.allMatch(student -> Objects.equals(1, student.getAge()));

以上就是 Java 流式开发的介绍了,基本覆盖了日常工作中需要用到的 Java 流式操作。

文章目录
  1. 1. Java 常见的Stream流式编程
  2. 2. 如何创建流?
    1. 2.0.0.1. 集合创建:
    2. 2.0.0.2. 数组创建:
  • 3. 中间操作
    1. 3.0.0.1. 过滤操作(filter)
    2. 3.0.0.2. 映射操作(map / flatMap)
    3. 3.0.0.3. mapToInt / mapToLong / mapToDouble 操作
    4. 3.0.0.4. 排序操作(sorted)
    5. 3.0.0.5. 去重操作(distinct)
    6. 3.0.0.6. 截断操作 / 跳过操作 / 观察操作(limit / skip / peek)
  • 4. 终端操作
    1. 4.0.0.1. 迭代操作(forEach)
    2. 4.0.0.2. 收集操作(collect)
    3. 4.0.0.3. partitioningBy(分组操作)
    4. 4.0.0.4. findFirst 操作
    5. 4.0.0.5. findAny 操作
    6. 4.0.0.6. anyMatch 操作
    7. 4.0.0.7. noneMatch 操作
    8. 4.0.0.8. allMatch 操作