当前位置: 首页 > article >正文

Java函数式编程【二】【Stream的装饰】【中间操作】【map映射器】【摊平映射器flatMap】

一、Java的Stream流式编程中的中间操作
Java的Stream流式编程中,中间操作是对数据流进行处理的一种方式,这些操作通常返回流对象本身,以便可以链接更多的操作。以下是一些常见的中间操作:

  • filter(Predicate predicate) - 用于通过设定的条件过滤出元素。

  • sorted() - 对元素进行排序。

  • distinct() - 去除重复的元素。

  • limit(long maxSize) - 获取指定数量的流元素。

  • skip(long n) - 跳过操作,跳过某些元素。

  • peek() - 查看操作。允许你在不影响流的主要处理逻辑的情况下,查看或使用流中的每个元素。这个方法可以用来进行一些调试或日志记录等操作。下面是一个示例:

peek例程

List<String> fruits = Arrays.asList("apple", "banana", "orange", "grape");
fruits.stream()
    .filter(f -> f.length() > 5)
    .peek(System.out::println)
    .collect(Collectors.toList());

中间操作的示例代码:

List<String> items = Arrays.asList("apple", "banana", "orange", "kiwi");
 
// Filtering
List<String> filteredItems = items.stream()
    .filter(item -> item.startsWith("a"))
    .collect(Collectors.toList());

// Sorting
List<String> sortedItems = items.stream()
    .sorted()
    .collect(Collectors.toList());
 
// Distinct
List<String> distinctItems = items.stream()
    .distinct()
    .collect(Collectors.toList());
 
// Limiting
List<String> limitedItems = items.stream()
    .limit(2)
    .collect(Collectors.toList());
 
// Skipping
List<String> skippedItems = items.stream()
    .skip(1)
    .collect(Collectors.toList());

在这里插入图片描述

函数式编程,在流管道中可以包含0个或n个中间操作,每个中间操作都返回一个新的流。

二、中间操作 映射器map()的用法

映射器map的方法签名(原型):

  • map(Function<? super T, ? extends R> mapper) - 它的输入参数是一个类型为函数接口的映射器,可将流中的元素转换成其他类型的元素。映射器map()是把一个对象流变成另一种对象流。

另外三个与映射器map()类似的映射器,它们则可以把一个对象流转换为基本类型流。

  • mapToInt(ToIntFunction<? super T> mapper) - 将元素映射为值的整型int流。
  • mapToLong(ToLongFunction<? super T> mapper) - 将元素映射为值的长整型long流。
  • mapToDouble(ToDoubleFunction<? super T> mapper) - 将元素映射为值的双精度浮点型double流。

在这里插入图片描述

用法一:将一种流映射成另一种流

把英文的小写转换为大写的代码片断:

// Mapping
List<String> items = Arrays.asList("apple", "banana", "orange", "grape");
List<String> mappedItems = items.stream()
    .map(String::toUpperCase)
    .collect(Collectors.toList());

下面是来看一个完整的示例。下图是这个流管道的详细分解说明图示:
在这里插入图片描述

这个示例完整的程序源码:

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MapTest {
	public static void main(String[] args) {
		List<User> list = Arrays.asList(new User(2001, "Ricky"), new User(2002, "Alex"), new User(2003, "Bob"));
		list.forEach(System.out::println);
		List<String> newList = list.stream().map(User::getName).sorted().limit(2).collect(Collectors.toList());
		newList.forEach(System.out::println);
	}
}

class User {
	private int id;
	private String name;
	public User(int id,String name) {
		this.id = id;
		this.name = name;
	}
	public String getName() {
		return name;
	}
	@Override
	public String toString() {
		return "{User[ID:"+id+" 姓名:"+name+"]}";
	}
}

例程测试效果图:
在这里插入图片描述
映射器map示例: 实现功能:整数列表每个元素+3。分别用对象流和IntStream来实现。
使用的两个映射器的原型如下所示:

//本示例使用的map()方法的签名,其入口参数类型是Function函数,如下:
<Integer> Stream<Integer> java.util.stream.Stream.map(Function<? super Integer, ? extends Integer> mapper)
//本示例使用的mapToInt()方法的签名,其入口参数类型是ToIntFunction函数,如下
IntStream java.util.stream.Stream.mapToInt(ToIntFunction<? super Integer> mapper)

这两个流,尽管入口参数的函数类型不一样,但完全可以用同一个Lambda表达式来代替函数接口的实例作为传入参数。
但后续的处理方式有些不同:映射器map()返回的是对象流,可用收集器collect(Collectors.toList())来收集结果。映射器mapToInt()返回的是IntStream(整型流),不能使用用收集器。因为收集器收集的元素必须是对象,IntStream中的元素是基本数据类型,所以收集器不支持。

		List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);

		//对象流的实现方式
		List<Integer> intListNew = intList.stream().map(x -> x + 3).collect(Collectors.toList());
		System.out.println("每个元素+3:" + intListNew);

		//用IntStream来实现
		System.out.println("打印IntStream:");
		IntStream intStream = intList.stream().mapToInt(x -> x + 3);
		intStream.forEach(System.out::println);

测试效果图:
在这里插入图片描述
mapToDouble基本数据类型流例程
基本数据类型流,比如DoubleStream(双精度浮点型流)不能使用收集器收集结果,但也有很多的终止操作,比如求最大最小值、求和、求平均值:

public static void main(String[] args) {
    List<Double> doubleList = Arrays.asList(1.0, 22.0, 3.0, 4.0, 32.0);
    double average = doubleList.stream().mapToDouble(Number::doubleValue).average().getAsDouble();
    double sum = doubleList.stream().mapToDouble(Number::doubleValue).sum();
    double max = doubleList.stream().mapToDouble(Number::doubleValue).max().getAsDouble();
    System.out.println("平均值:" + average + ",总和:" + sum + ",最大值:" + max);
}

测试结果: 平均值:12.4,总和:62.0,最大值:32.0

三、中间操作 摊平映射器flatMap()的用法

  • flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) - 将每个元素转换为某种元素类型的流,然后将它们连接成一个流。

在这里插入图片描述

摊平映射器flatMap()示例一:

public class StreamTest {
	public static void main(String[] args) {
		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(",");
			return Arrays.stream(split);
		}).collect(Collectors.toList());

		System.out.println("处理前的集合:" + list);
		System.out.println("处理后的集合:" + listNew);
	}
}

在这个例子中,初始时对象流的元素类型是长度为7的字符串:“m,k,l,a"和"1,3,5,7”。[ “m,k,l,a” , “1,3,5,7” ]。通过摊平映射器flatMap()转换后的对象流的元素类型是长度为1的字符串。最终对象流为[ “m” , “k” , “l” , “a” , “1” , “3” , “5” , “7” ]

实际上,上面的示例程序还可以简化,可简化为:

	List<String> listNew = list.stream().flatMap(str->Stream.of(str.split(","))).collect(Collectors.toList());

摊平映射器flatMap()示例二:

package stream;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class FlatMapTest {
	public static void test1() {
		List<String> items = Arrays.asList("glass", "brick","gold", "silver");
		List<String> flatMappedItems = items.stream().flatMap(item -> Stream.of(item.split(""))).collect(Collectors.toList());
		flatMappedItems.forEach(System.out::println);
	}

	public static void test2() {
		String songs = "Remember me to one who lives there";
		String[] words = songs.split(" ");
		List<String> strList = Arrays.stream(words).map(s -> s.split("e"))
				.flatMap(e -> Arrays.stream(e))
				.collect(Collectors.toList());
		strList.forEach(System.out::println);
	}
	public static void main(String[] args) {
		test1();
		test2();
	}
}

测试结果图:
在这里插入图片描述
流的连接:有两种方式。如果是两个流的连接,可使用 Stream.concat() 方法;
如果是三个及三个以上流的连接,就使用 摊平映射器flatMap() 方法。
摊平映射器flatMap还可以用来实现流的连接,请看例程:

	public static void concatStream() {
		String names[][] = { {"Alice", "Alien", "Bob"},
			{"Jack", "John", "Jobs"},{"Maria", "Golf", "Korea"} };
        //两个流的连接
        Stream<String> concat = Stream.concat(Arrays.stream(names[0]), Arrays.stream(names[1]));
        concat.forEach(System.out::println);
        System.out.println("===========");
        //多个流的连接,例子之一
        List<String[]> list = Arrays.asList(names);
        Stream<String> strStream = list.stream().flatMap(e->Arrays.stream(e));
        strStream.forEach(System.out::println);
        
        System.out.println("===========");
        Stream<String> first = Stream.of("Alice", "Alien", "Bob");
        Stream<String> second = Stream.of("Jack", "John", "Jobs");
        Stream<String> third = Stream.of("Maria", "Golf", "Korea");
        //多个流的连接,例子之二
        //Stream<String> stringStream = Stream.of(first, second, third).flatMap(s->s);
        Stream<String> stringStream = Stream.of(first, second, third).flatMap(Function.identity());
        stringStream.forEach(System.out::println);
	}

说明: 多个流的连接,“例子之一”和“例子之二”实现相同功能。另外,示例中“flatMap(Function.identity())”等价于“flatMap(s->s)”。

摊平映射器有很多,例如摊平为基本数据类型流的映射器(可由类型T的对象流转换为基本数据类型的流):

  • IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
  • LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
  • DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);

映射器map和flatMap的区别

map()操作将每个元素转换成一个新元素,并将所有这些新生成的元素收集到一个新的流中。
flatMap()操作将每个元素转换成一个新的流,并将所有这些新生成的流合并成一个单一的流。
flatMap()和map()之间还有一个重要的区别,那就是flatMap()支持处理包含嵌套数据结构的流。

参考文献:

  • Java8 Stream:2万字20个实例,玩转集合的筛选、归约、分组、聚合-CSDN
  • 恕我直言你可能真的不会java系列-CSDN
  • Java8 Stream流使用-CSDN
  • Java 8 Stream API:从基础到高级,掌握流处理的艺术
  • JAVA8专题-Stream流操作详解
  • Java 8新特性Stream()流
  • Java8 特性笔记(四) Stream

第1篇:lambda表达式会用了么?
第2篇:Java Stream API?
第3篇:Stream的Filter与谓词逻辑
第4篇:Stream管道流Map操作
第5篇:Stream的状态与并行操作
第7篇:像使用SQL一样排序集合
第8篇-函数式接口
第9篇-元素的匹配与查找
第10篇-集合元素归约
第11篇-Stream API终端操作

Java Stream函数式编程第三篇:管道流结果处理


http://www.kler.cn/a/420500.html

相关文章:

  • 【浏览器】缓存与存储
  • 106.【C语言】数据结构之二叉树的三种递归遍历方式
  • 计算机网络——不同版本的 HTTP 协议
  • Jmeter进阶篇(28)结合AI做性能测试:开启性能测试自动化新篇章
  • 嵌入式蓝桥杯学习1 点亮LED
  • JavaWeb12
  • IPv6 如何实现网络安全?
  • 统信服务器操作系统V20系列配置JDK方案
  • 【java-Neo4j 5进阶篇】- 1.批量新增数据
  • 1128作业
  • Debezium Engine监听binlog实现缓存更新与业务解耦
  • redhat 7.9配置阿里云yum源
  • Android 原生解析 Json 字符串
  • 《Vue零基础入门教程》第十五课:样式绑定
  • 黑马程序员MybatisPlus/Docker相关内容
  • MFC工控项目实例三十四模拟量实时监控数字显示效果
  • Git Bash + VS Code + Windows11 Git命令报错莫名奇妙的问题
  • 数据库(学习笔记)
  • YOLOv11 NCNN安卓部署
  • 【CVPR24】OmniMedVQA: 一种新的医疗LVLM大规模综合评估基准
  • 【笔记】文明、现代化与价值投资
  • 【C++boost::asio网络编程】有关异步读写api的笔记
  • 再谈Java中的String类型是否相同的判断方法
  • ESP32-S3模组上跑通ES8388(11)
  • git bash 一双击选中内容就^C (ctrl C)
  • 安全关系型数据库查询新选择:Rust 语言的 rust-query 库深度解析