“ 在上一篇的文章中,我们聊到Stream创建的四种方式,以及中间操作筛选与切片,那么今天我们来看一下映射和排序”
01
—
映射流
在探究Java8的Stream(一)中我们说到了映射API,稍微再提一下:
1.map(Function f) 接收一个函数作为参数,该函数会被应用到每个元 素上,并将其映射成一个新的元素
2.mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 DoubleStream
3.mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 IntStream
4.mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元 素上,产生一个新的 LongStream
5.flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
那么我们为什么需要映射呢?
某些场景下,我们可能拿到的存储对象的数组,但是可能其中对象中的某一个字段对我们是有用的,那怎么办呢?通常我们可能会遍历这个数组然后依次取出对象,但是映射可以帮助我们把这个字段映射到新的流中。也就是说通过map可以把一个流的元素弄到另一个流中,把流中的一些满足条件的元素放到一个新流里面。
我们来看一个小例子(StreamObject是上一节中的对象):
public static void map() {
List<StreamObject> list = Arrays.asList(
new StreamObject(1, "每天学Java", "提示Java水平", "想让自己变得更有价值"),
new StreamObject(2, "每天学Java", "提示Java水平", "想让自己变得更有价值")
);
list.stream()
.map(StreamObject::getId)
.forEach(System.out::println);
}
//或是这种形式
list.stream()
.map(n->{return n.getId();})
.forEach(System.out::println);
}
输出结果
Connected to the target VM, address: '127.0.0.1:49871', transport: 'socket'
1
2
Disconnected from the target VM, address: '127.0.0.1:49871', transport: 'socket'
可以看出来map将流中的id通过map弄到一个新的流中,然后输出流出来,这里从map的方法中我们可以看出来,它接受一个函数作为参数,然后对其中每个元素进行映射,进而返回了一个新的流,这个流就叫做映射流(R指定新流的元素类型,T指定调用流的元素类型,mapper是完成映射的Function实例,被称为映射函数)。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
mapToInt(),mapToLong(),mapToDouble这里就不说了,跟map很像,我们下面来看一下flatMap()这个方法。
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
提供的映射函数会处理原始流中的每一个元素,而映射流中包含了所有经过我们映射函数处理后产生的新元素。flatMap()操作能把原始流中的元素进行一对多的转换,并且将新生成的元素全都合并到它返回的流里面。
我们通过看一个例子来解释一下这句话是什么意思
public static void flatMap() {
List<StreamObject> list = Arrays.asList(
new StreamObject(1, "每天学Java", "提示Java水平", "想让自己变得更有价值"),
new StreamObject(2, "每天学Java", "提示Java水平", "想让自己变得更有价值")
);
list.stream().flatMap(StreamObject::filter).forEach(System.out::println);
}
其中filter方法是返回一个字符串:
public static Stream<String> filter(StreamObject str) {
return Stream.of(str.getName());
}
输出结果
Connected to the target VM, address: '127.0.0.1:50220', transport: 'socket'
每天学Java
每天学Java
Disconnected from the target VM, address: '127.0.0.1:50220', transport: 'socket'
Process finished with exit code 0
乍一看跟上面的map没有什么区别,首先我们看一下引用方法的区别:
map方法中引入函数返回对象可以是int,但是flatmap方法中的函数返回对象必须是Stream。此外:flatMap与map的区别在于 flatMap是将一个流中的每个值都转成一个个流,然后再将这些流扁平化成为一个流 。
这里用一个网上的例子供大家看一下(原文链接:https://blog.csdn.net/jiangpingjiangping/article/details/76392904)
package test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import net.sf.json.JSONArray;
public class Test {
public static void main(String[] args) {
List<String> strs = Arrays.asList("好,好,学", "习,天,天", "向,上");
List<String[]> strArray = strs.stream().map(str -> str.split(",")).collect(Collectors.toList());
JSONArray jsonArray = JSONArray.fromObject(strArray);
System.out.println("strArray => " + jsonArray.toString());
List<String> strList = strs.stream().map(str -> str.split(",")).flatMap(Arrays::stream)
.collect(Collectors.toList());
System.out.println("strList => " + strList);
}
运行结果:
strArray => [["好","好","学"],["习","天","天"],["向","上"]] strList => [好, 好, 学, 习, 天, 天, 向, 上]
映射就说到这里,希望小伙伴们可以私下去练习一下,下面我们再来看一下排序。
02
—
排序
排序相较于前面的映射要简单很多:
1.sorted() 产生一个新流,其中按自然顺序排序
2.sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序
看下面的例子:
public static void sorted() {
List<StreamObject> list = Arrays.asList(
new StreamObject(1, "每天学Java", "提示Java水平", "想让自己变得更有价值"),
new StreamObject(4, "每天学Java", "提示Java水平", "想让自己变得更有价值"),
new StreamObject(2, "每天学Java", "提示Java水平", "想让自己变得更有价值")
);
list.stream().map(StreamObject::getId).sorted().forEach(System.out::println);
list.stream().sorted((x, y) -> {
if (x.getId() == y.getId()) {
return x.getName().compareTo(y.getName());
} else {
return x.getId() > y.getId() ? 1 : -1;
}
}).forEach(System.out::println);
}
结果:
Connected to the target VM, address: '127.0.0.1:50793', transport: 'socket'
1
2
4
Disconnected from the target VM, address: '127.0.0.1:50793', transport: 'socket'
StreamObject{id=1, name='每天学Java', aim='提示Java水平', reason='想让自己变得更有价值'}
StreamObject{id=2, name='每天学Java', aim='提示Java水平', reason='想让自己变得更有价值'}
StreamObject{id=4, name='每天学Java', aim='提示Java水平', reason='想让自己变得更有价值'}
Process finished with exit code 0
关于排序就不多说来,大家可以很容易理解上面的例子,一个自然排序,一个通过比较器进行排序。
03
—
Stream的终止操作
在前面我们已经聊到Stream的创建和中间操作(嗲用的方法返回一个新流,它就算是一个中间操作)。那么在Stream篇的最后我们来看一下Stream的终止操作。
终止操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void ,如果流操作后返回的值还是Stream流类型的,则是开始操作和中间操作。
那么看一下终止操作有哪些API
首先是查找与匹配操作API
allMatch——检查是否匹配所有元素 anyMatch——检查是否至少匹配一个元素 noneMatch——检查是否没有匹配的元素 findFirst——返回第一个元素 findAny——返回当前流中的任意元素 count——返回流中元素的总个数 max——返回流中最大值 min——返回流中最小值
归约操作API
reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 T reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 Optional< T>
收集操作API
collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的 实现,用于给Stream中元素做汇总的方法
那么这些API要如何运用呢,这里就举个例子供大家参考。
/*
终止操作
*/
public static void endOper() {
List<StreamObject> list = Arrays.asList(
new StreamObject(1, "每天学Java", "提示Java水平", "想让自己变得更有价值"),
new StreamObject(4, "每天学Java", "提示Java水平", "想让自己变得更有价值"),
new StreamObject(2, "每天学Java", "提示Java水平", "想让自己变得更有价值")
);
//检查是否匹配所有元素
boolean b = list.stream().allMatch(n -> {
return n.getName().equals("每天学Java");
});
boolean b2 = list.stream().allMatch(n -> {
return n.getId() == 1;
});
System.out.println(b + "," + b2);
//检查是否至少匹配一个元素
boolean b3 = list.stream().anyMatch(n -> {
return n.getName().equals("每天学Java1");
});
boolean b4 = list.stream().anyMatch(n -> {
return n.getId() == 1;
});
System.out.println(b3 + "," + b4);
//检查是否没有匹配所有元素
boolean b5 = list.stream().noneMatch(n -> {
return n.getName().equals("每天学Java1");
});
boolean b6 = list.stream().noneMatch(n -> {
return n.getId() == 1;
});
System.out.println(b5 + "," + b6);
//返回第一个元素
Optional<StreamObject> f = list.stream().findFirst();
System.out.println(f);
//返回任意一个元素 注意这里中的是parallelStream并行流
Optional<StreamObject> f1 = list.parallelStream().findAny();
System.out.println(f1);
//返回流中元素总数
long l = list.stream().count();
System.out.println(l);
//返回流中最大值
int max1 = list.stream().map(StreamObject::getId).max((x, y) -> Integer.compare(x, y)).get();
System.out.println(max1);
//返回流中最小值
int min1 = list.stream().map(StreamObject::getId).min((x, y) -> Integer.compare(x, y)).get();
System.out.println(min1);
//内部迭代(使用 Collection 接口需要用户去做迭 代,称为外部迭代。相反,Stream API 使用内部 迭代——它帮你把迭代做了)
list.stream().forEach(System.out::println);
//reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 T
//这里参数中的数值 也会被加进去
Integer i = list.stream().map(StreamObject::getId).reduce(1, (x, y) -> x + y);
Integer i1 = list.stream().map(StreamObject::getId).reduce((x, y) -> x + y).get();
System.out.println(i);
System.out.println(i1);
//collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的 实现,用于给Stream中元素做汇总的方法
List<String> listString = list.stream().map(StreamObject::getName).collect(Collectors.toList());
System.out.println(listString);
}
那么关于Stream新特性Stream就到这里结束了。
跟小伙伴们分享一下这一周的推送安排,大家可以重点关注自己喜欢的文章(已经发布的大家可以直接点击链接进去哦):
九月10号周一:探究Java8的Stream(一)
九月11号周二:探究Java8的Stream(二)
九月12号周三:关系型数据库之oracle
九月13号周四:探究Java8的Optional 类
九月14号周五:Effective Java(第三版)——条目十四:考虑实现Comparable接口
那么今天小程序更新的题库是什么呢?
今天小程序更新的题目是:
1.HashMap是一个高效通用的数据结构,你能说一说为什么吗?
2.哈希碰撞会对hashMap的性能带来什么的影响
3.Java8中有没有对哈希碰撞做了优化?
4.Java8对哈希碰撞做了优化有什么用处?
5.Java8为什么废除永久代
6.你知道Java8并发包下的LongAdder吗?
7.StampedLock和ReadWriteLock有什么关系
8.聊一聊String.intern()方法的认知
对答案有兴趣的小伙伴可以进入小程序查看答案哦!