使用Java 8 Stream像操作SQL一样处理数据(下)

转载自 使用Java 8 Stream像操作SQL一样处理数据(下)

在上一篇文章中,我们介绍了Stream可以像操作数据库一样来操作集合,但是我们没有介绍 flatMap 和 collect 操作。这两种操作对实现复杂的查询是非常有用的。比如你可以结果 flatMap 和 collect 计算stream中的单词的字符数,像下面代码那样。

import static java.util.function.Function.identity;
import static java.util.stream.Collectors.*;Stream<String> words = Stream.of("Java", "Magazine", "is", "the", "best");Map<String, Long> letterToCount =words.map(w -> w.split("")).flatMap(Arrays::stream).collect(groupingBy(identity(), counting()));

上述代码的运行结果是:

[a:4, b:1, e:3, g:1, h:1, i:2, ..]

这篇文章将会介绍flatMap和collect这两种操作的更多细节。

flatMap操作

假设你在一个文章中查找一个单词,你会怎么做?

我们可以使用 Files.lines() 方法,因为它可以返回一个文章一行一行信息组成的stream。我们可以使用 map() 把文章的每行分割是很多单词,最后,使用`distinct()``移除重复的。我们将想法转化为代码:

Files.lines(Paths.get("stuff.txt")).map(line -> line.split("\\s+")) .distinct() // Stream<String[]>.forEach(System.out::println);

很不幸,这样并不正确。如果你运行得到这样的结果:

[Ljava.lang.String;@7cca494b
[Ljava.lang.String;@7ba4f24f

到底发生了什么事呢?问题出在使用的lambda表达式将会把文件的每行转化成一个字符串数组(String[])。这就导致map返回的是一个Stream<String[]>类型的结果,我们实际上需要的是一个Stream类型的结果。

我们需要一串的单词,而不是一串的数组。对于数组可以使用 Arrays.stream() 将数组变成一个stream。看下面的实现:

String[] arrayOfWords = {"Java", "Magazine"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);

如果我们使用下面方式的话其实还有不起作用的,这是因为使用 map(Arrays::stream) 后返回的其实是Stream<Stream>类型。

Files.lines(Paths.get("stuff.txt")).map(line -> line.split("\\s+")) // Stream<String[]>.map(Arrays::stream) // Stream<Stream<String>>.distinct() // Stream<Stream<String>>.forEach(System.out::println);

我们可以使用flatMap来解决这种问题,像下面这样。使用flatMap方法的作用是返回的是stream中的内容而不是一个stream。

Files.lines(Paths.get("stuff.txt")).map(line -> line.split("\\s+")) // Stream<String[]>.flatMap(Arrays::stream) // Stream<String>.distinct() // Stream<String>.forEach(System.out::println);

collect 操作

我们来具体看一下collect操作。上面文章中看到了返回stream的操作(说明该操作是一个中间操作)和返回一个值、boolean型值、int型值和Optional型值的操作(说明该操作是终结操作) 。

将Stream中的元素转化到集合中.

使用toSet()你可以把一个stream转化成一个不包含重复项的集合。下面的代码展示了怎么生成高消费(单笔交易>1000$)城市的集合。

Set<String> cities = transactions.stream().filter(t -> t.getValue() > 1000).map(Transaction::getCity).collect(toSet());

注意这样你不能保证返回什么类型的Set,你可以使用toCollection()来提高可控性。比如你可以像下面代码这样将一个HashSet的构造方法作为参数。

Set<String> cities = transactions.stream().filter(t -> t.getValue() > 1000).map(Transaction::getCity).collect(toCollection(HashSet::new));

collect操作方法不止这些,上面介绍的只是很小一部分,还可以实现这些功能:

  • 通过货币类型进行分组,计算各种获取类型的交易总金额(将会返回一个 Map<Currency, Integer>)

  • 将所有交易分类两组:大金额的和非大金额的(将会返回一个Map<Boolean, List>)

  • 创建多级分组,比如先根据城市分组,然后再根据是否为大金额交易分组( 将会返回一个Map<String, Map<Boolean, List>>)

让我们看一下Stream API和集合器怎么实现这些查询,我们先对一个stream中的数据进行计算平均值,最大值和最小值。接下来我们再看如果实现简单的分组,最后我们我们将多个集合器放在一起实现强大的查询功能,比如多级分组。

Summarizing

有很多预定义的集合器和是很方便的使用,比如使用counting() 计算个数:

long howManyTransactions = transactions.stream().collect(counting());

你可以对Double, Int, 或者Long属性的元素进行 summing Double(), summingInt(), and summingLong() 操作,像下面这样:

int totalValue = transactions.stream().collect(summingInt(Transaction::getValue));

类似的你还可以使用averagingDouble(), averagingInt(), and averagingLong() 计算平均值,像下面这样:

double average = transactions.stream().collect(averagingInt(Transaction::getValue));

还可以通过使用maxBy()和minBy()计算元素中的最大值和最小值,不过你需要定义一个做比较的 比较器,所以maxBy和minBy需要一个Comparator对象最为参数:

下面的例子中我们使用了静态方法comparing(),它将根据传递进去的参数生成一个Comparator对象。这个方法根据提取stream中元素的可以做比较的key来做判断。在这个例子中是通过银行交易的金额大小来做比较的。

Optional<Transaction> highestTransaction = transactions.stream().collect(maxBy(comparing(Transaction::getValue)));

还有一个叫reducing()的集合器,它可以通过重复地对stream中的所有元素进行一种操作指导产生一个结果。它和reduce()有点类似。比如下面的代码使用reducing()方法计算交易的总金额。

 

int totalValue = transactions.stream().collect(reducing(0, Transaction::getValue, Integer::sum));

reducing() 有三个参数:

  • 初始值(如果stream是空也将返回该值):这里是0
  • 一个会被应用到各个元素的方法
  • 结合两个提取出来的值,这是是将两个值加起来

Grouping

一个常规的数据库操作就是根据一个属性对数据进行分组。比如根据货币对交易进行分组,如果使用迭代那简直太复杂了:

Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();for(Transaction transaction : transactions) { Currency currency = transaction.getCurrency();List<Transaction> transactionsForCurrency = transactionsByCurrencies.get(currency);if (transactionsForCurrency == null) {transactionsForCurrency = new ArrayList<>();transactionsByCurrencies.put(currency, transactionsForCurrency);}transactionsForCurrency.add(transaction);
}

Java 8 中有一个叫 groupingBy() 的集合器,我们可以像这样做查询:

groupingBy() 方法有一个提取分类key的函数做参数,我们可以叫它为分类函数。在这个例子中我们使用的是Transaction::getCurrency来实现根据货币分组。

Map<Currency, List<Transaction>> transactionsByCurrencies =transactions.stream().collect(groupingBy(Transaction::getCurrency));

Partitioning

还有一个叫做partitioningBy()的函数,这个可以看做是groupingBy()的特例。它需要一个predicate(返回一个boolean的函数)作为参数,将会对stream中的元素根据是否满足predicate进行分类。partitioning可以将stream变成一个 Map<Boolean, List>。使用代码如下:

Map<Boolean, List<Transaction>> partitionedTransactions =transactions.stream().collect(partitioningBy( t -> t.getValue() > 1000));

如果要要对不同货币的金额进行求和操作在SQL中可以结合使用SUM和GROUP BY。那我们使用Stream API也能这么做吗?当然可以了,像下面这样使用:

之前使用的groupingBy (Transaction::getCity)其实是groupingBy (Transaction::getCity, toList())的速写方式。

Map<String, Integer> cityToSum = transactions.stream().collect(groupingBy(Transaction::getCity, summingInt(Transaction::getValue)));

再看一个例子,如果你要统计每个城市的交易最大值,可以做这样实现:

Map<String, Optional<Transaction>> cityToHighestTransaction = transactions.stream().collect(groupingBy(Transaction::getCity, maxBy(comparing(Transaction::getValue))));

再看一个更加复杂的例子,在刚才的例子中我们给groupingBy传递了另外一个集合器作为参数来进一步对元素进行分组。由于groupingBy本身是一个集合器,我们可以通过传递其他groupingBy集合器来创建多级分组,被传递进来的这个groupingBy定义了一个二级标准可以对stream中的元素进行再分组。

下面代码中我们先对城市进行分组,然后我们再根据每个城市的交易不同货币的平均值进行分组

Map<String, Map<Currency, Double>> cityByCurrencyToAverage = transactions.stream().collect(groupingBy(Transaction::getCity,groupingBy(Transaction::getCurrency, averagingInt(Transaction::getValue))));

 

自定义集合器

 

我们看到的这些集合器都实现了 java.util.stream .Collector 接口。这就意味着你可以自定义集合器。

总结

这篇文章中,我们探索了两个Stream API的高级操作:flatMap和colelct。通过这两个操作你可以创建更加复杂的数据处理查询。

我们还通过collect方法实现了summarizing, grouping, 和 partitioning 操作。这些操作还可以被结合起来创建更加复杂的查询。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331140.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring(4)面向切面的Spring(AOP)

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review “spring(4)面向切面的Spring&#xff08;AOP&#xff09;”的相关知识&#xff1b;2&#xff09;在软件开发中&#xff0c;散布于应…

Mybatis-plus 思维导图,让 Mybatis-plus 不再难懂

转载自 Mybatis-plus 思维导图&#xff0c;让 Mybatis-plus 不再难懂 摘要: Mybatis-Plus&#xff08;简称MP&#xff09;是一个Mybatis的增强工具&#xff0c;在 Mybatis 的基础上只做增强不做改变&#xff0c;为简化开发、提高效率而生。mybatis-plus与mybatis mybatis Mybat…

报错:The type javax.servlet.http.HttpServletRequest cannot be resolved

【0】README 0.1&#xff09;以下内容转自&#xff1a; http://blog.csdn.net/vincent080928/article/details/5392891 problem&#xff09;The type javax.servlet.http.HttpServletRequest cannot be resolved. It is indirectly referenced from required .class files. 这…

MyBatis 思维导图,让 MyBatis 不再难懂(一)

转载自 MyBatis 思维导图&#xff0c;让 MyBatis 不再难懂&#xff08;一&#xff09;写在前面与hibernate相比&#xff0c;我无疑更喜欢mybatis&#xff0c;就因为我觉得它真的好用&#xff0c;哈哈。它简单上手和掌握&#xff1b;sql语句和代码分开&#xff0c;方便统一管理和…

tomcat(20)基于JMX的管理

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“深入剖析tomcat”&#xff0c;旨在学习“tomcat(20)基于JMX的管理” 的相关知识&#xff1b;2&#xff09;晚辈我在tomcat上部署web 项目以测试JMX管理 tomcat 容器bean的效果&#xff0c;结果运行不成功&#xff0…

mybatis思维导图,让mybatis不再难懂(二)

转载自 mybatis思维导图&#xff0c;让mybatis不再难懂&#xff08;二&#xff09; 写在前面 上一篇文章写了mybatis的基本原理和配置文件的基本使用&#xff0c;这一篇写mybatis的使用&#xff0c;主要包括与sping集成、动态sql、还有mapper的xml文件一下复杂配置等。值得注意…

spring(5)构建 spring web 应用程序

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review “spring(5)构建 spring web 应用程序” 的相关知识&#xff1b;【1】spring mvc 起步【1.1】跟踪spring mvc的请求1&#xff09;请求…

Spring思维导图,让Spring不再难懂(ioc篇)

转载自 Spring思维导图&#xff0c;让Spring不再难懂&#xff08;ioc篇&#xff09; 写过java的都知道&#xff1a;所有的对象都必须创建&#xff1b;或者说&#xff1a;使用对象之前必须先创建。而使用ioc之后&#xff0c;你就可以不再手动创建对象&#xff0c;而是从ioc容器中…

spring(6) 渲染web视图

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review “spring(6) 渲染web视图” 的相关知识&#xff1b;【1】 理解视图解析【1.1】视图解析的基础知识以及spring 提供的其他视图解析器1…

Spring思维导图,让Spring不再难懂(aop篇)

转载自 Spring思维导图&#xff0c;让Spring不再难懂&#xff08;aop篇&#xff09; 什么是aop AOP&#xff08;Aspect-OrientedProgramming&#xff0c;面向方面编程&#xff09;&#xff0c;可以说是OOP&#xff08;Object-Oriented Programing&#xff0c;面向对象编程&…

tomcatSupplement(1)tomcat启动脚本分析(以Windows平台为例)

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“深入剖析tomcat”&#xff0c;旨在学习“tomcat启动脚本分析” 的相关知识&#xff1b;2&#xff09;for tomcat4 startup files&#xff0c; please visit https://github.com/pacosonTang/HowTomcatWorks/tree/ma…

Spring思维导图,让spring不再难懂(一)

转载自 Spring思维导图&#xff0c;让spring不再难懂&#xff08;一&#xff09; 摘要: Spring是一个开源框架&#xff0c;是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构&#xff0c;分层架构允许您选择使用哪一个组件&#xff0c;同时为 J2EE …

spring(7)spring mvc 的高级技术

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review “spring(7)spring mvc 的高级技术” 的相关知识&#xff1b;2&#xff09;本文将会看到如何编写控制器来处理文件上传&#xff0c;如…

Ubuntu下MySQL、Redis以及MongoDB三个数据库的启动、重启以及停止命令

一、MySQL #启动 /etc/init.d/mysql start #停止 /etc/init.d/mysql stop #重启 /etc/init.d/mysql restart 二、Redis #启动 redis-server #停止 pkill redis-server三、MongoDB #启动服务 sudo service mongod start #重启 sudo service mongod restart #终止 sudo serv…

Spring思维导图,让Spring不再难懂(mvc篇)

转载自 Spring思维导图&#xff0c;让Spring不再难懂&#xff08;mvc篇&#xff09;spring mvc简介与运行原理Spring的模型-视图-控制器&#xff08;MVC&#xff09;框架是围绕一个DispatcherServlet来设计的&#xff0c;这个Servlet会把请求分发给各个处理器&#xff0c;并支持…

spring(10)通过spring 和 JDBC征服数据库

【0】README1&#xff09;本文部分文字描述转自&#xff1a;“Spring In Action&#xff08;中/英文版&#xff09;”&#xff0c;旨在review “spring(10)通过spring 和 JDBC征服数据库” 的相关知识&#xff1b;【1】 spring 的数据访问哲学1&#xff09;intro&#xff1a;s…

Ubuntu下apt-get方式Git的安装、配置和更新

一、首先命令行下载Git sudo apt-get update sudo apt-get install git 通过此种方式安装的git可能不是官方的最新版本&#xff0c;通过 git --version 查看版本信息&#xff0c;然后与git官网比对官网下载地址 二、然后配置git的帐号信息 git config --global user.name &q…

Spring思维导图,让Spring不再难懂(cache篇)

转载自 Spring思维导图&#xff0c;让Spring不再难懂&#xff08;cache篇&#xff09; 关于缓存 缓存是实际工作中非常常用的一种提高性能的方法。而在java中&#xff0c;所谓缓存&#xff0c;就是将程序或系统经常要调用的对象存在内存中&#xff0c;再次调用时可以快速从内存…

intro to JNDI

【0】README 1&#xff09;本文转自 http://sishuok.com/forum/blogPost/list/1186.html 【1】原文如下 1&#xff09;JNDI 是什么&#xff1f; JNDI是 Java 命名与目录接口&#xff08;Java Naming and Directory Interface&#xff09;&#xff0c;在J2EE规范中是重要的规范之…

Mybatis3配置方法

一.准备 1.三个Jar包&#xff0c; 1.mybatis-3.2.1.jar 2.mysql-connector-java-5.1.12-bin.jar 3.log4j-1.2.17.jar 2.vo类对象 public class SysUser {private Long id;private String name;private String loginName;private String password;private String avatar;/** ge…