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函数式编程第三篇:管道流结果处理