如何用Java Stream 写出 “高效率、干净、简洁” 的代码?

Java 常见的Stream流式编程

  • Java 8 添加了一个新的抽象称为流 Stream API,可以让你以一种声明的方式处理数据。

  • Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

  • Stream API 可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

如何创建流?

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

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

java 复制代码
/**
* 数组创建流
*/
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 的元素过滤出来,并打印结果。

java 复制代码
/**
* 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)

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

(1)map 映射操作:将 lists 流中的字符串,处理成大写的字符串;

(2)flatMap 映射操作:将 lists 流中的二位数组,转成一维数组。(flatMap 用于将流中的一个元素转换为一个流,然后将所有转换后的流组装成一个流)

java 复制代码
/**
* 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 类型,并进一步做数值计算。

java 复制代码
public class Student{
    //年龄
    private int age;
    //姓名
    private String Name;
}
public static void mapTest(){
	int totalAge1 = students.stream().mapToInt(Student::getAge).sum();
    int totalAge2 = students.stream().mapToLong(Student::getAge).sum();
    int totalAge3 = students.stream().mapToDouble(Student::getAge).sum();
}
  • 排序操作(sorted)

​ 排序流中的元素。

java 复制代码
/**
* 排序操作 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)

​ 去除流中的重复元素。

java 复制代码
/**
* 去重操作(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)
java 复制代码
/**
* 截断操作(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)

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

java 复制代码
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 函数,对流中的元素进行收集。

java 复制代码
// 将学生流转换为 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(分组操作)
java 复制代码
// 通过学生的年龄是否等于1进行分组,等于则分为 true 组,不等于则分为 false 组
Map<Boolean,List<Student>> partStudent = students.stream().collect(Collectors.partitioningBy(student -> Objects.equals(1,student.getAge())));
  • findFirst 操作

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

java 复制代码
Student firstStu = students.stream()  
        .filter(student -> Objects.equals(1, student.getAge()))  
        .findFirst().orElse(null);
  • findAny 操作

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

java 复制代码
Student student = students.stream()  
        .filter(student -> Objects.equals(1, student.getAge())) 
        .findAny().orElse(null);
  • anyMatch 操作

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

java 复制代码
boolean result = students.stream()
		.anyMatch(student -> Objects.equals(1, student.getAge()));
  • noneMatch 操作

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

java 复制代码
boolean result = students.stream()
		.noneMatch(student -> Objects.equals(1, student.getAge()));
  • allMatch 操作

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

java 复制代码
boolean result = students.stream()
		.allMatch(student -> Objects.equals(1, student.getAge()));

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

相关推荐
考虑考虑37 分钟前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干1 小时前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying1 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·1 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
martinzh2 小时前
Spring AI 项目介绍
后端
Bug退退退1232 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠2 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github
前端付豪2 小时前
20、用 Python + API 打造终端天气预报工具(支持城市查询、天气图标、美化输出🧊
后端·python
爱学习的小学渣2 小时前
关系型数据库
后端
武子康3 小时前
大数据-33 HBase 整体架构 HMaster HRegion
大数据·后端·hbase