如何用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 流式操作。

相关推荐
编程乐趣9 小时前
基于.Net开发的数据库导入导出的开源项目
后端
赵星星5209 小时前
别再搞混了!深入浅出理解Java线程中start()和run()的本质区别
java·后端
Ray669 小时前
FST
后端
白露与泡影9 小时前
SpringBoot 自研运行时 SQL 调用树,3 分钟定位慢 SQL!
spring boot·后端·sql
花花无缺10 小时前
接口(interface)中的常量和 类(class)中的常量的区别
java·后端
舒一笑10 小时前
利用Mybatis自定义排序规则实现复杂排序
后端·排序算法·mybatis
毕设源码-郭学长10 小时前
【开题答辩全过程】以 基于vue+springboot的校园疫情管理系统的设计与实现为例,包含答辩的问题和答案
java·spring boot·后端
中国lanwp10 小时前
Tomcat 中部署 Web 应用
java·前端·tomcat
Joey_Chen10 小时前
【Golang开发】快速入门Go——Goroutine和Channel
后端·go
岁忧10 小时前
(LeetCode 每日一题) 36. 有效的数独 (数组、哈希表)
java·c++·算法·leetcode·go·散列表