原稿于3.2日发布,然而事情并没完,我发现必须得补充一个常见的坑,为了防止大家采坑,我在更新了本文的后两段。

上一篇中我们通过一个实例看到了Java8 Stream API 相较于传统的的Java 集合操作的简洁与优势,本篇我们依然借助于一个实际的例子来看看Java8 Stream API 如何抽取及收集数据。

备注:上一篇内容:如何用Java8 Stream API找到心仪的女朋友

目标 & 背景

我们以“处理订单数据”为例,假设我们的应用是一个分布式应用,有"订单应用","物流应用","商品应用”等都是独立的服务。本次我们的目的需要展示订单列表完整数据:

  • 1.查询订单列表。
  • 2.批量查询物流信息。
  • 3.将物流信息填充到订单主信息中。

假设我们定义了一个订单类,具有几个关键的属性:订单号,状态,订单价,快递信息。如下所示:

class Order{
String orderSeq;
String status;
double totalPrice;
String expressInfo;
// 省略get,set及hashCode等方法
}

我们定义了一个快递信息类,几个关键的属性:订单号,物流公司,物流单号,物流状态。如下所示:

class ExpressInfo{
String orderSeq;
String expressName;
String expressNo;
String createTime;
String statusInfo;
// 省略get,set及hashCode等方法
}

Java7 实现

获取订单列表 & 抽取订单号

   List<Order> orderList = getOrderList();
// 抽取 订单号
List<String> orderSeqList = new ArrayList<>();
for (Order order : orderList) {
orderSeqList.add(order.getOrderSeq());
}

这里我们获取了订单列表orderList,此时expressInfo里边是没有数据的。这里抽取单号依然是Java传统的写法。

批量查询快递信息 & 组装 订单-快递信息 map

由于我们是通过调用远程服务来获取快递信息,为了减少网络通信次数,我们采取批量查询的方式。这也是为什么,上一步中我们要抽取订单号

下面我们来获取快递信息

// 调用远程服务,
List<ExpressInfo> expressInfos = RpcGetExpressInfoBatch(orderSeqList);
// 组装 订单-快递 关系map
Map<String,String> orderExpressMap = new HashMap<>();
for(ExpressInfo e: expressInfos){
orderExpressMap.put(e.getOrderSeq(),e.getStatusInfo());
}

这里组装map,也依然是Java7常用的写法。

组合数据,将快递信息填充进订单

for(Order order:orderList){
String expressInfo = orderExpressMap.get(order.getOrderSeq());
order.setExpressInfo(expressInfo);
}

至此,我们使用Java7 的写法,完成了开篇设定的目标。下面我们看Java8的写法

Java8 实现

获取订单列表 & 抽取订单号

// 获取列表
List<Order> orderList = getOrderList();
// 抽取单号
List<String> orderSeqs = orderList.stream()
.map(Order::getOrderSeq)
.collect(Collectors.toList());

这里我们使用了stream.map,在map()中,我们的写法是Order::getOrderSeq表示调用Order对象的getOrderSeq()方法来抽取订单号。

这里的::叫“方法引用”,是Java8中的新写法。

map()后面紧跟的是collect收集器,他将抽取的数据toList(),于是我们得到了最终的List。

批量查询快递信息 & 组装 订单-快递信息 map

下面我们仍然是通过远程调用来获取快递信息,然后使用Java8的语法建立一个 订单-快递 关联信息的map。

List<ExpressInfo> expressInfos = RpcGetExpressInfoBatch(orderSeqList);
Map<String,String> orderExpressMap =expressInfos.stream()
.collect(Collectors.toMap(ExpressInfo::getOrderSeq,ExpressInfo::getStatusInfo));

这里代码比Java7的要少吧,且一目了然,这里用strean().collect来收集数据,收集成什么形式呢?看名知意,Collectors.toMap收集成Map,收集什么数据呢?toMap()中,写了ExpressInfo::getOrderSeqExpressInfo::getStatusInfo,即:抽取orderSeq作为key,statusInfo作为value。

通过这里的Collectors.toMap收集器,我们很方便的获得了 订单-物流关系数据map

组合数据,将快递信息填充进订单

经过上面的两步,我们得到了符合我们要求的数据,现在我们需要将快递信息填充进订单,代码如下:

orderList.stream().forEach(o -> o.setExpressInfo(orderExpressMap.get(o.getOrderSeq())));

你没看错,就只有这么一行。

补充说明: key 重复的处理

上面的代码中,我们使用如下代码来从list中收集Map:

Collectors.toMap(ExpressInfo::getOrderSeq,ExpressInfo::getStatusInfo))

这里如果expressInfos这个list中有重复数据,那么orderSeq就会有重复的,这种情况下就会报一个错:

java.lang.IllegalStateException: Duplicate key

想模拟这个错误很简单,往expressInfos中add两条一样的数据,运行上面的抽取map代码就会报错。

为了解决这个问题,我们需要这样写:

List<ExpressInfo> expressInfos = RpcGetExpressInfoBatch(orderSeqList);
Map<String,String> orderExpressMap =expressInfos.stream()
.collect(Collectors.toMap(ExpressInfo::getOrderSeq,ExpressInfo::getStatusInfo,(v1,v2)->v1));

这里,我们主要的变动是这一句:

Collectors.toMap(ExpressInfo::getOrderSeq,ExpressInfo::getStatusInfo,(v1,v2)->v1)

我们在在Collectors.toMap中增加了一个lumda表达式:

(v1,v2)->v1

意思是当有重复数据v1,v2出现的时候,我们选择v1。当然,你可以根据你的情况选择v2。

选择v1,或者v2,需要根据你的业务来权衡。比如list中的数据按照时间先后排序的,我们取最新的就选v2,反之取v1。

总结

本节,我们使用Java8 Stream API,完成了数据的抽取和收集,使用了map(),和collect()来完成数据的抽取和收集,并了解了两种收取方式toListtoMap。同时我们也知道应对list中有重复数据导致报错的问题。所以,以后如果有人问你"Java8 stream 如何获取对象的某个属性list啊?",“java8 stream 如何获取指定数据组装成map啊?”,你就可以把本文中的方法告诉他了。

除此之外,Java8 Streap API 还有分组 等功能,后面再说。你也可以关注我的公众号,第一时间收到推送。

Java8系列- 如何用Java8 Stream API找到心仪的女朋友

SpringMVC是怎么工作的,SpringMVC的工作原理

Mybatis Mapper接口是如何找到实现类的-源码分析

小程序云开发:菜鸟也能全栈做产品

CORS详解,CORS原理分析

工作6年,失业19天

Docker & k8s 系列一:快速上手docker

Docker & k8s 系列二:本机k8s环境搭建

Docker & k8s 系列三:在k8s中部署单个服务实例

何用Java8 Stream API进行数据抽取与收集的更多相关文章

  1. 如何用Java8 Stream API找到心仪的女朋友

    传统的的Java 集合操作是有些啰嗦的,当我们需要对结合元素进行过滤,排序等操作的时候,通常需要写好几行代码以及定义临时变量. 而Java8 Stream API 可以极大简化这一操作,代码行数少,且 ...

  2. 使用Java8 Stream API对Map按键或值进行排序

    一.什么是Java 8 Stream 使用Java 8 Streams,我们可以按键和按值对映射进行排序.下面是它的工作原理: 将Map或List等集合类对象转换为Stream对象 使用Streams ...

  3. Fork/Join框架与Java8 Stream API 之并行流的速度比较

    Fork/Join 框架有特定的ExecutorService和线程池构成.ExecutorService可以运行任务,并且这个任务会被分解成较小的任务,它们从线程池中被fork(被不同的线程执行)出 ...

  4. 1.分类维护-通过Java8 Stream API 获取商品三级分类数据

    实体类 @Data @TableName("pms_category") public class CategoryEntity implements Serializable { ...

  5. Java8 Stream API

    Stream是Java8中,操作集合的一个重要特性. 从iteration到Stream操作 当你操作一个集合的时候,你通常的做法是迭代每一个元素,然后处理你想要的事情.举个例子: String co ...

  6. java8 stream api流式编程

    java8自带常用的函数式接口 Predicate boolean test(T t) 传入一个参数返回boolean值 Consumer void accept(T t) 传入一个参数,无返回值 F ...

  7. JAVA8 Stream API的使用

    /** * @auther hhh * @date 2018/12/31 12:48 * @description Stream流:用来处理数组.集合的API * 1.不是数据结构,没有内部存储(只是 ...

  8. java8 Stream API笔记

    生成Stream Source的方式 从Collection和数组生成 * Collection.stream() * Collection.parallelStream() * Arrays.str ...

  9. 【Java8新特性】面试官:谈谈Java8中的Stream API有哪些终止操作?

    写在前面 如果你出去面试,面试官问了你关于Java8 Stream API的一些问题,比如:Java8中创建Stream流有哪几种方式?(可以参见:<[Java8新特性]面试官问我:Java8中 ...

随机推荐

  1. 函数动态传参,命名空间,gloabal,nonlocal关键字

    一.函数参数->动态传参(形参的第三种) 动态参数分为两种: 1)动态接收位置参数 普通的位置传参: def func(quality_food,junk_food): print('我要吃', ...

  2. Spring IOC 和AOP

    Spring是什么? Spring是一个轻量级的IoC和AOP容器框架. IOC:IOC就是控制反转,控制反转指的是把创建对象和管理对象之间的依赖关系交给了IOC容器来管理.以前new对象由程序员来控 ...

  3. 以puppeteer抓取微指数,puppeteer基本示例,docker部署headless

    还是直接上代码 https://github.com/cclient/weizhishu-puppeteer 根据关键字获取微博指数 早期版本以常规的构造cookie,token,sign的访问api ...

  4. Codeforces Round #572 (Div. 1) 差E

    Codeforces Round #572 (Div. 1) A2 题意:给一棵树,带边权,互不相同且为偶数.每次操作是选两个叶子然后在路径上同时加一个数.初始边权全是0,求一个操作序列使得最终边权与 ...

  5. 安装Rational Rose启动报错:无法启动此程序,因为计算机中丢失 suite objects.dll。

    运行Rational Rose的时候如果出现这样的错误,先检查环境变量有没有common的地址,如果没有直接配上就OK:配置如下:D:\Program Files\Rational\Common; 我 ...

  6. Create Collection Type with Class Type Constraints

    Create Collection Type with Class Type Constraints: new TypeToken<ArrayList<ClassType>>( ...

  7. NIO与IO

    待续... 该文章部分摘自:http://tutorials.jenkov.com/java-nio/index.html 一.I/O简介 I/O(英语:Input/Output),即输入/输出, 指 ...

  8. 推荐几款开源的js日期控件

    做为一个正规的网站,经常需要一些日期或时间的筛选,所以我们今天就推荐二十多款javascript的js日期/时间筛选插件.个个经典,绝对有你需要的. My97DatePicker ,国人开发的一款js ...

  9. 抽样分布|t分布|中心极限定理|点估计|矩估计|最大似然法|

    生物统计与实验设计-统计学基础-2&区间估计-1 正态分布参数:均值和方差 其中,选择1d是因为好算:通常,95%区分大概率事件和小概率事件, 当总体是正态分布时,可以利用常用抽样分布估计出样 ...

  10. 自主知识产权受热捧 瑞星ESM SOHO版全力护卫小微企业

    小微企业现在可以说是我国国民经济中最重要的组成部分,在总产值.利税.解决就业等方面,都在为国家积极贡献着自己的力量.但在小微企业一片欣欣向荣的背后,却有着难言之隐--那就是困扰着广大小微企业多年的企业 ...