前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >探究Java8的Stream(二)

探究Java8的Stream(二)

作者头像
每天学Java
发布2020-06-01 17:21:55
5480
发布2020-06-01 17:21:55
举报
文章被收录于专栏:每天学Java

在上一篇的文章中,我们聊到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是上一节中的对象):

代码语言:javascript
复制
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);
    }

输出结果

代码语言:javascript
复制

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实例,被称为映射函数)。

代码语言:javascript
复制
 <R> Stream<R> map(Function<? super T, ? extends R> mapper);

mapToInt(),mapToLong(),mapToDouble这里就不说了,跟map很像,我们下面来看一下flatMap()这个方法。

代码语言:javascript
复制
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

提供的映射函数会处理原始流中的每一个元素,而映射流中包含了所有经过我们映射函数处理后产生的新元素。flatMap()操作能把原始流中的元素进行一对多的转换,并且将新生成的元素全都合并到它返回的流里面。

我们通过看一个例子来解释一下这句话是什么意思

代码语言:javascript
复制
  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方法是返回一个字符串:

代码语言:javascript
复制
  public static Stream<String> filter(StreamObject str) {

        return Stream.of(str.getName());
    }

输出结果

代码语言:javascript
复制
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)

代码语言:javascript
复制
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) 产生一个新流,其中按比较器顺序排序

看下面的例子:

代码语言:javascript
复制
 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);
    }

结果:

代码语言:javascript
复制
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要如何运用呢,这里就举个例子供大家参考。

代码语言:javascript
复制
  /*
      终止操作
     */
    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()方法的认知

对答案有兴趣的小伙伴可以进入小程序查看答案哦!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-09-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 每天学Java 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档