java8 如何在java streamm之后判断使用哪个方法

java8 stream学习笔记
为了更好地对集合进行并行操作,java8中加入了stream API。以前对集合的操作策略由客户端提供,有了stream API后,对集合的操作集成到了集合内部,客户端只需要按需调用即可。stream API支持函数式编程,它对集合的操作一般分为三个阶段:
(1)source:即集合的开始状态。
(2)intermedia&operations :0个或者多个中间阶段,比如Stream.filter,Stream.map等,中间阶段通常都是lazy,比如filter操作并不会马上开始过滤,而是返回一个新的stream对象。直到遇到terminal operation时才会真正地执行。
(3)terminal operation:一个终结操作,比如foreach,IntStream.sum。
以代码为例分析stream的执行过程:
public static void main(String[] args) {
List&String& words = asList(&aboutjava&, &accessibility&, &addressing&, &addshortcut&, &about&, &all&, &become&,&bacteriumparatyphosum&);
// 以a开头的字符串的最大长度
int result = words.stream().filter(s -& s.startsWith(&a&)).mapToInt(s -& s.length()).max().getAsInt();
System.out.println(result);
}以上代码是计算字符串列表中以a开头的字符串的最大长度,一共包括两个intermedia&operations:filter和mapToInt,一个terminal operation:max。
如下图所示,每一个操作都会创建一个Stream Pipeline,每个Pipeline包含一个Sink(也就是具体操作),max实际上是由reduce操作来实现的,Stream Pipeline通过upstream字段形成一个链表。
当调用max()方法时,PipelineHelper.wrapAndCopyInto()方法会被调用,这个方法的实现如下:
final &P_IN, S extends Sink&E_OUT&& S wrapAndCopyInto(S sink, Spliterator&P_IN& spliterator) {
copyInto(wrapSink(Objects.requireNonNull(sink)), spliterator);
}主要做了两件事:
1、将所有的Sink(Ops)进行合并,合并的代码如下:
final &P_IN& Sink&P_IN& wrapSink(Sink&E_OUT& sink) {
Objects.requireNonNull(sink);
for ( @SuppressWarnings(&rawtypes&) AbstractPipeline p=AbstractPipeline. p.depth & 0; p=p.previousStage) {
sink = p.opWrapSink(binedFlags, sink);
return (Sink&P_IN&)
}这些Sink最终会通过downstream形成一个链表。
举个栗子,要从豆腐中过滤出豆浆,通常的做法是准备好几张过滤网(每张过滤网的功能不一样,有的是过滤豆腐渣的,有的可能是过滤沙,有的过滤其他更小的杂质),将豆腐倒入第一个网中,用桶接住,将桶中的豆腐再倒入第二张网中。。。依次类推,最后得到豆浆。
这里合并Sink的过程,就是把所有过滤网叠在一起,看起来像一张网但兼有以上几张网的功能,以后只需要过滤一次就可以得到豆浆了。
2、遍历集合,代码如下:
if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags())) {
wrappedSink.begin(spliterator.getExactSizeIfKnown());
spliterator.forEachRemaining(wrappedSink);
wrappedSink.end();
copyIntoWithCancel(wrappedSink, spliterator);
}wrappedSink.begin会依次调用所有Sink的begin方法,相当于叠好网,放到指定位置。
spliterator.forEachRemaining会依赖调用Sink的accept方法,相当于把豆腐倒在网上进行过滤。
这里有对Short_circuit类型的操作进行特殊处理,比如findFirst只需要找一个满足要求的,当找到一个后,就不再找了。好比桶中装满一杯豆浆后,就不倒豆腐了。
wrappedSink.end表明已处理完,直接出结果。
在Stream的这些操作中,有的是无状态的,有的是有状态的,有状态的操作包括:distinct、sorted、limit、skip,比如distince依赖之前的结果,否则不知道当前结果是否唯一,这种情况下线程之前会有竞争,从而影响并发的性能,因此并行情况下要谨慎使用有状态的操作。
short-circuiting
有时候需要在遍历中途停止操作,比如查找第一个满足条件的元素或者limit操作。在Stream中short-circuiting操作有:anyMatch、allMatch、noneMatch、findFirst、findAny、limit,这些操作在Sink中都有一个变量来判断是否短路,比如limit用的是m,match用的是stop,find用的是hasValue。Sink中的cancellationRequested方法就是用来针对short-circuiting操作的,一旦发现是短路操作,就会调用AbstractPipeline的copyIntoWithCancel方法:
final &P_IN& void copyIntoWithCancel(Sink&P_IN& wrappedSink, Spliterator&P_IN& spliterator) {
@SuppressWarnings({&rawtypes&,&unchecked&})
AbstractPipeline p = AbstractPipeline.
while (p.depth & 0) {
p = p.previousS
wrappedSink.begin(spliterator.getExactSizeIfKnown());
p.forEachWithCancel(spliterator, wrappedSink);
wrappedSink.end();
}其中forEachWithCancel方法会跳过后面所有的Sink:
final void forEachWithCancel(Spliterator&Integer& spliterator, Sink&Integer& sink) {
Spliterator.OfInt spl = adapt(spliterator);
IntConsumer adaptedSink = adapt(sink);
do { } while (!sink.cancellationRequested() && spl.tryAdvance(adaptedSink));
在Stream中存在两种顺序:
(1)encounter order:如果集合本身有序(比如:list),返回的结果就按集合的顺序;
(2)thread order :在并行情况下,集合被分成几部分分别在不同的线程中执行,有可能处于后面的元素先处理,这种线程顺序也称为时间顺序。
以下代码中forEach打印的结果是乱序的,forEachOrdered是有顺序的。
public static void main(String[] args) {
List&Integer& list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
list.stream().parallel().forEach(x -& System.out.print(x));
list.stream().parallel().forEachOrdered(x -& System.out.print(x));
}除了使用forEachOrdered保证顺序外,Collectors.toList()也可以保证顺序,二都最终都是通过ForEachOrderedTask类来实现的,具体可以参看ForEachOp.ForEachOrderedTask类中的代码。
看过本文的人也看了:
我要留言技术领域:
取消收藏确定要取消收藏吗?
删除图谱提示你保存在该图谱下的知识内容也会被删除,建议你先将内容移到其他图谱中。你确定要删除知识图谱及其内容吗?
删除节点提示无法删除该知识节点,因该节点下仍保存有相关知识内容!
删除节点提示你确定要删除该知识节点吗?博客分类:
我们经常会用到各种集合,数字的,字符串的还有对象的。它们无处不在,哪怕操作集合的代码要能稍微优化一点,都能让代码清晰很多。在这章中,我们探索下如何使用lambda表达式来操作集合。我们用它来遍历集合,把集合转化成新的集合,从集合中删除元素,把集合进行合并。
遍历列表是最基本的一个集合操作,这么多年来,它的操作也发生了一些变化。我们使用一个遍历名字的小例子,从最古老的版本介绍到现在最优雅的版本。
用下面的代码我们很容易创建一个不可变的名字的列表:
final List&String& friends =
Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");
System.out.println(friends.get(i));
下面这是最常见的一种遍历列表并打印的方法,虽然也最一般:
for(int i = 0; i & friends.size(); i++) {
System.out.println(friends.get(i));
我把这种方式叫做自虐型写法——又啰嗦又容易出错。我们得停下来好好想想,"是i&还是i&=呢?"这只有当我们需要操作具体某个元素的时候才有意义,不过即便这样,我们还可以使用坚持不可变原则的函数式风格来实现,这个我们很快会讨论到。
Java还提供了一种相对先进的for结构。
collections/fpij/Iteration.java
for(String name : friends) {
System.out.println(name);
在底层,这种方式的迭代是使用Iterator接口来实现的,调用了它的hasNext和next方法。 这两种方式都属于外部迭代器,它们把如何做和想做什么揉到了一起。我们显式的控制迭代,告诉它从哪开始到哪结束;第二个版本则在底层通过Iterator的方法来做这些。显式的操作下,还可以用break和continue语句来控制迭代。 第二个版本比第一个少了点东西。如果我们不打算修改集合的某个元素的话,它的方式比第一个要好。不过这两种方式都是命令式的,在现在的Java中应该摒弃这种方式。 改成函数式原因有这几个:
for循环本身是串行的,很难进行并行化。
这样的循环是非多态的;所得即所求。我们直接把集合传给for循环,而不是在集合上调用一个方法(支持多态)来执行特定的操作。
从设计层面来说,这样 写的代码违反了“Tell,Don't Ask”的原则 。我们请求执行一次迭代,而不是把迭代留给底层库来执行。
是时候从老的命令式编程转换到更优雅的内部迭代器的函数式编程了。使用内部迭代器后我们把很多具体操作都扔给了底层方法库来执行,你可以更专注于具体的业务需求。底层的函数会负责进行迭代的。我们先用一个内部迭代器来枚举一下名字列表。
Iterable接口在JDK8中得到加强,它有一个专门的名字叫forEach,它接收一个Comsumer类型的参数。如名字所说,Consumer的实例正是通过它的accept方法消费传递给它的对象的。我们用一个很熟悉的匿名内部类的语法来使用下这个forEach方法:
friends.forEach(new Consumer&String&() { public void accept(final String name) {
System.out.println(name); }
我们调用了friends集合上的forEach方法,给它传递了一个Consumer的匿名实现。这个forEach方法从对集合中的每一个元素调用传入的Consumer的accept方法,让它来处理这个元素。在这个示例中我们只是打印了一下它的值,也就是这个名字。 我们来看下这个版本的输出结果,和上两个的结果 是一样的:
我们只改了一个地方:我们抛弃了过时的 for循环,使用了新的内部迭代器。好处是,我们不用指定如何迭代这个集合,可以更专注于如何处理每一个元素。缺点是,代码看起来更啰嗦了——这简直要把新的编码风格带来的喜悦冲的一干二净了。所幸的是,这个很容易改掉,这正是lambda表达式和新的编译器的威力大展身手的时候了。我们再做一点修改,把匿名内部类换成lambda表达式。
friends.forEach((final String name) -& System.out.println(name));
这样看起来就好多了。代码更少了,不过我们先来看下这是什么意思。这个forEach方法是一个高阶函数,它接收一个lambda表达式或者代码块,来对列表中的元素进行操作。在每次调用的时候 ,集合中的元素会绑定到name这个变量上。底层库托管了lambda表达式调用的活。它可以决定延迟表达式的执行,如果合适的话还可以进行并行计算。 这个版本的输出也和前面的一样。
内部迭代器的版本更为简洁。而且,使用它的话我们可以更专注每个元素的处理操作,而不是怎么去遍历——这可是声明式的。
不过这个版本还有缺陷。一旦forEach方法开始执行了,不像别的两个版本,我们没法跳出这个迭代。(当然有别的方法能搞定这个)。因此,这种写法在需要对集合里的每个元素处理的时候比较常用。后面我们会介绍到一些别的函数可以让我们控制循环的过程。
lambda表达式的标准语法,是把参数放到()里面,提供类型信息并使用逗号分隔参数。Java编译器为了解放我们,还能自动进行类型推导。不写类型当然更方便了,工作少了,世界也清静了。下面是上一个版本去掉了参数类型之后的:
friends.forEach((name) -& System.out.println(name));
在这个例子里,Java编译器通过上下文分析,知道name的类型是String。它查看被调用方法forEach的签名,然后分析参数里的这个函数式接口。接着它会分析这个接口里的抽象方法,查看参数的个数及类型。即便这个lambda表达式接收多个参数,我们也一样能进行类型推导,不过这样的话所有参数都不能带参数类型;在lambda表达式中,参数类型要么全不写,要写的话就得全写。
Java编译器对单个参数的lambda表达式会进行特殊处理:如果你想进行类型推导的话,参数两边的括号可以省略掉。
friends.forEach(name -& System.out.println(name));
这里有一点小警告:进行类型推导的参数不是final类型的。在前面显式声明类型例子中,我们同时也把参数标记为final的。这样能防止你在lambda表达式中修改参数的值。通常来说,修改参数的值是个坏习惯,这样容易引起BUG,因此标记成final是个好习惯。不幸的是,如果我们想使用类型推导的话,我们就得自己遵守规则不要修改参数,因为编译器可不再为我们保驾护航了。
走到这步可费了老劲了,现在代码量确实少了一点。不过这还不算最简。我们来体验下最后这个极简版的。
friends.forEach(System.out::println);
在上面的代码中我们用到了一个方法引用。我们用方法名就可以直接替换整个的代码了。在下节中我们会深入探讨下这个,不过现在我们先来回忆下Antoine de Saint-Exupéry的一句名言:完美不是无法再增添加什么,而是无法再去掉什么。
lambda表达式让我们能够简洁明了的进行集合的遍历。下一节我们会讲到它如何使我们在进行删除操作和集合转化的时候,也能够写出如此简洁的代码。
浏览 22102
somefuture
浏览: 557096 次
来自: 上海
请问,对于自定义的登录页面,我的ajax请求方式该怎么写呢,是 ...
翻译的内容在哪里?
关键是这有啥用?
我用的比较传统的方法,就是写一个supportDao的类,把基 ...
spiniper 写道说实话,spring-data并不是很好 ...
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'Java8中使用流方式查询数据库的方法
字体:[ ] 类型:转载 时间:
这篇文章主要介绍了Java8中使用流方式查询数据库的相关资料,需要的朋友可以参考下
由于关系型数据库操作语言和面向对象语言之间的差异,如今我们仍然需要花费许多时间建立数据库与 Java 应用之间互相沟通的桥梁。通常,我们可以编写自己的映射层(mapping layer),或者使用第三方的 ORM(Object Relational Mapper)对象关系映射框架,比如 Hibernate。ORM 框架虽然使用起来很方便,但是如何正确地配置和提高框架操作数据库的性能却不太容易,ORM 框架往往会使我们的应用性能下降。 最近,我贡献了一个新的开源项目——Speedment,它能使我们使用 Java 8 开发数据库应用程序变得更为快捷和高效。
Speedment 是使用 ORM 方式操作数据库的一种选择,以前我们需要100行操作数据库的 Java 代码,在 Java 8中,可能只需要一行代码。
在90年代末,我使用 Java 开发数据库应用的时候,许多代码逻辑都需要自己来编写,比如捕获异常、类型转换等,经过许多改动,最后这些代码变得难以维护和扩展。
由于关系型数据库操作语言和面向对象语言之间的差异,如今我们仍然需要花费许多时间建立数据库与 Java 应用之间互相沟通的桥梁。通常,我们可以编写自己的映射层(mapping layer),或者使用第三方的 ORM(Object Relational Mapper)对象关系映射框架,比如 Hibernate。ORM 框架虽然使用起来很方便,但是如何正确地配置和提高框架操作数据库的性能却不太容易,ORM 框架往往会使我们的应用性能下降。
最近,我贡献了一个新的开源项目——Speedment,它能使我们使用 Java 8 开发数据库应用程序变得更为快捷和高效。
Speedment 是什么?
Speedment 是一个开源项目,它是一个基于 Java 8 的新特性开发的新的 Java 库,从这个项目开发开始,它的代码就全部使用 Java 8来编写。Speedment 使用标准流查询数据库,这使得开发者不需要学习任何新的查询 API ,以及不必考虑 JDBC 、ResultSet 和其他有关数据库的指定的操作。
Speedment 会根据现有数据库来自动生成代码。由于它的这种方式,开发者不需要编写一行关于数据库实体(database entities)的代码。它生成的代码中也包含 JavaDocs 帮助文档,这使开发者不需要编写关于 User 或者 Book 等对象的实体类。取而代之地,我们只需要创建或者使用一个现有的数据库,然后用 Speedment 去连接它,接着 Speedment 会分析数据库结构来生成实体类的代码。
更有趣的是,Speedment 用野兔来作为它的吉祥物。 在接下来的例子中,我们会使用一个名为 “hare” 的数据库来给大家演示 Speedment 的使用方式。该数据库的表结构如下:
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(45) | NO | | NULL | |
| color | varchar(45) | NO | | NULL | |
| age | int(11) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)
下面是 Speedment 根据数据库信息生成的一个相应的实体类(为简洁起见,我们将 JavaDocs 在这里移除了):
public interface Hare extends Entity&Hare& {
public final static ReferenceComparableField&Hare, Integer& ID = new ReferenceComparableFieldImpl&&("id", Hare::getId, Hare::setId);
public final static ReferenceComparableStringField&Hare& NAME = new ReferenceComparableStringFieldImpl&&("name", Hare::getName, Hare::setName);
public final static ReferenceComparableStringField&Hare& COLOR = new ReferenceComparableStringFieldImpl&&("color", Hare::getColor, Hare::setColor);
public final static ReferenceComparableField&Hare, Integer& AGE = new ReferenceComparableFieldImpl&&("age", Hare::getAge, Hare::setAge);
Integer getId();
String getName();
String getColor();
Integer getAge();
Hare setId(Integer id);
Hare setName(String name);
Hare setColor(String color);
Hare setAge(Integer age);
/** Graph-like traversal methods eliminating JOINs */
Stream&Carrot& findCarrotsByOwner();
Stream&Carrot& findCarrotsByRival();
Stream&Carrot& findCarrots();
我将用一篇单独的文章介绍 find*() 方法的用法,它可以被用来代替 SQL joins 操作。
Queries查询示例
下面的例子展示如何查询 Hare 表的数据库信息:
List&Hare& oldHares = hares.stream()
.filter(AGE.greaterThan(8))
.collect(toList());
上面的代码看起来已经遍历了 hare 数据库表的所有行,但实际上并不是这样的。 Stream 是智能的,当它到达 collect() 操作的时候,会分析 filter 操作,并推断出 hare.age 大于8的列,因此会节省 hares 的流操作,产生与 “select * from hare where age & 8” 操作一样的效果。如果你使用了多个 filters,他们会被合并起来以节省流操作。下面是另一种用流方式进行多个操作的例子:
long noOldHares = hares.stream()
.filter(AGE.greaterThan(8))
.mapToInt(Hare::getAge)
在上面的代码中,当流到达 count() 操作时,它将检查它自己的管道。首先会推断上面例子中的 AGE 操作,其次在不改变 count() 结果的情况下,会推断 mapToInt() 和 sorted() 操作,这些操作可以被消除,因此这段代码的操作被节省为 “select count(*) from hare where age & 8”。这意味着您可以使用 Java 8 流而你不必如此在意流是如何转换为SQL的。
如何下载和加入我们
如果你想学习如何使用 Speedment 的 API 和在项目中如何使用 Speedment,可以访问网址 www.speedment.org,并可以在 gitter 上发表评论,也可以从 GitHub 上下载 Speedment 的源码,来贡献你自己的代码。
回顾早期的一些老项目,一个超过100行代码的数据库类,现在可以使用 Java 8 缩减成1行代码。那是反转后的摩尔定律,在14年内(=7摩尔周期),行数大约减半了七次。这就是进步!
什么是数据流
流代表从支持聚合操作源的序列的对象。以下是数据流的特点。
元素序列 - 流提供了一组特定类型的以顺序方式元素。流获取/计算需求的元素。它不存储元素。
源- 流使用集合,数组或I/O资源为输入源。
聚合操作 - 数据流支持如filter, map, limit, reduced, find, match等聚合操作。
管道传输 - 大多数流操作的返回流本身使他们的结果可以被管道传输。这些操作被称为中间操作以及它们的功能是利用输入,处理输入和输出返回到目标。collect()方法是终端操作,这是通常出现在管道传输操作结束标记流的结束。
自动迭代 - 流操作内部做了反复对比,其中明确迭代需要集合提供源元素。
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具流操作在本人接触到的部分,都是处理集合容器Collection,加入流操作主要是为了函数式编程,在很大程度上可以简化代码
简洁的代码处理复杂逻辑这是每个程序猿的追求,废话不多说,开始介绍
使用Stream基本步骤
1. 创建Stream;
2. 转换Stream,每次转换原有Stream对象不改变,返回一个新的Stream对象(**可以有多次转换**);
3. 对Stream进行聚合(Reduce)操作,获取想要的结果;
Stream 创建
//Lists是Guava中的一个工具类
List&Integer& nums = Lists.newArrayList(1,null,3,4,null,6);
nums.stream().filter(num -& num != null).count();&pre name=&code& class=&java&&/**
of方法:有两个overload方法,一个接受变长参数,一个接口单一值
Stream&Integer& integerStream = Stream.of(1, 2, 3, 5);
Stream&String& stringStream = Stream.of(&taobao&);
generator方法:生成一个无限长度的Stream,其元素的生成是通过给定的Supplier
Stream.generate(new Supplier&Double&() {
public Double get() {
return Math.random();
Stream.generate(() -& Math.random());
Stream.generate(Math::random);
iterate方法:也是生成无限长度的Stream,和generator不同的是,其元素的生成是重复对给定的种子值(seed)
调用用户指定函数来生成的。其中包含的元素可以认为是:seed
Stream.iterate(1, item -& item + 1).limit(10).forEach(System.out::println);
所有集合类都可以直接调用stream()方法返回一个Stream对象
这个在本文的第一个例子中就展示了从List对象获取其对应的Stream对象,如果查看Java doc就可以发现Collection接口有一个stream方法,所以其所有子类都都可以获取对应的Stream对象。
public interface Collection&E& extends Iterable&E& {
//其他方法省略
default Stream&E& stream() {
return StreamSupport.stream(spliterator(), false);
}转换Stream
转化Stream方法有很处理方法,此处全部介绍,介绍几种,其余方法的使用都差不多
1. distinct: 对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素;
List&String& list = Arrays.asList(&aa&, &bb&, &cc&, &a&, &b&, &c&, &aa&, &ab&, &cc&, &bb&, &bc&);
list = list.stream().distinct().collect(Collectors.toList()); // [aa, bb, cc, a, b, c, ab, bc]
2. filter: 对于Stream中包含的元素使用给定的过滤函数进行过滤操作,新生成的Stream只包含符合条件的元素
List&String& list = Arrays.asList(&aa&, &bb&, &cc&, &a&, &b&, &c&,&a&, &aa&, &ab&, &cc&, &bb&, &bc&);
list.stream().filter(e -& e.length()&=2).forEach(e -& System.out.print(e + &,&));// aa,bb,cc,aa,ab,cc,bb,bc,
3. map: 对于Stream中包含的元素使用给定的转换函数进行转换操作,新生成的Stream只包含转换生成的元素。这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong和mapToDouble。这三个方法也比较好理解,比如mapToInt就是把原始Stream转换成一个新的Stream,这个新生成的Stream中的元素都是int类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗;
&pre name=&code& class=&java&&List&Integer& integerList = Arrays.asList(1, 2, 3, 8, 9, 6, 4, 2, 3, 7, 6);integerList.stream().map(var -& { var ++; var += 2; }).forEach(System.out::print);
DoubleStream doubleStream = integerList.stream().mapToDouble((value) -& value * 1.0);
4. flatMap:和map类似,不同的是其每个元素转换得到的是Stream对象,会把子Stream中的元素压缩到父集合中;
List&Integer& together = Stream.of(asList(1, 2),asList(3, 4)).flatMap(numbers -& numbers.stream()).collect(toList());
将两个队列一起处理流操作的时候,可以利用此方法将其转化为一个流
上部分的代码可以简化为
List&Integer& together = Stream.of(Arrays.asList(1, 2),Arrays.asList(3, 4))
.flatMap(Collection::stream).collect(Collectors.toList());5. peek: 生成一个包含原Stream的所有元素的新Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数;
此方法不会更改流中元素形式,主要用于调试或输出。
list.stream().distinct().peek(e -& System.out.println(e.length())).count();
在peek()方法中使用lambda 表达式的时候,返回值只能为void类型&
方法暂时只介绍这么几个其余还有 limit() &skip() & forEach()等
聚合(Reduce)操作
下面会分两部分来介绍汇聚操作:
1. 可变汇聚:把输入的元素们累积到一个可变的容器中,比如Collection或者StringBuilder;
List&Integer& nums = Lists.newArrayList(1,1,null,2,3,4,null,5,6,7,8,9,10);
List&Integer& numsWithoutNull = nums.stream().filter(num -& num != null).
collect(() -& new ArrayList&Integer&(),
(list, item) -& list.add(item),
(list1, list2) -& list1.addAll(list2));上面这段代码就是对一个元素是Integer类型的List,先过滤掉全部的null,然后把剩下的元素收集到一个新的List中。进一步看一下collect方法的三个参数,都是lambda形式的函数(*上面的代码可以使用方法引用来简化,留给读者自己去思考*)。
第一个函数生成一个新的ArrayList实例;
第二个函数接受两个参数,第一个是前面生成的ArrayList对象,二个是stream中包含的元素,函数体就是把stream中的元素加入ArrayList对象中。第二个函数被反复调用直到原stream的元素被消费完毕;
第三个函数也是接受两个参数,这两个都是ArrayList类型的,函数体就是把第二个ArrayList全部加入到第一个中;
但是上面的collect方法调用也有点太复杂了,没关系!我们来看一下collect方法另外一个override的版本,其依赖
A& R collect(Collector&?&super&T,
A, R& collector);
Java8还给我们提供了Collector的工具类–[Collectors]其中已经定义了一些静态工厂方法,比如:Collectors.toCollection()收集到Collection中,
Collectors.toList()收集到List中和Collectors.toSet()收集到Set中。这样的静态方法还有很多,这里就不一一介绍了,大家可以直接去看JavaDoc。下面看看使用Collectors对于代码的简化:
List&Integer& numsWithoutNull = nums.stream().filter(num -& num != null).
collect(Collectors.toList());
2. 其他汇聚:除去可变汇聚剩下的,一般都不是通过反复修改某个可变对象,而是通过把前一次的汇聚结果当成下一次的入参,反复如此。比如reduce,count,allMatch;
reduce方法:reduce方法非常的通用,后面介绍的count,sum等都可以使用其实现。reduce方法有三个override的方法,本文介绍两个最常用的,最后一个留给读者自己学习。先来看reduce方法的第一种形式,其方法定义如下:
Optional&T& reduce(BinaryOperator&T& accumulator);接受一个BinaryOperator类型的参数,在使用的时候我们可以用lambda表达式来。
List&Integer& ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println(&ints sum is:& + ints.stream().reduce((sum, item) -& sum + item).get());可以看到reduce方法接受一个函数,这个函数有两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数。要注意的是:**第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素**。这个方法返回值类型是Optional,这是Java8防止出现NPE的一种可行方法,后面的文章会详细介绍,这里就简单的认为是一个容器,其中可能会包含0个或者1个对象。
reduce方法还有一个很常用的变种:
T reduce(T identity, BinaryOperator&T& accumulator);这个定义上上面已经介绍过的基本一致,不同的是:它允许用户提供一个循环计算的初始值,如果Stream为空,就直接返回该值。而且这个方法不会返回Optional,因为其不会出现null值。下面直接给出例子,就不再做说明了。
List&Integer& ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println(&ints sum is:& + ints.stream().reduce(0, (sum, item) -& sum + item));
count方法:获取Stream中元素的个数。比较简单,这里就直接给出例子,不做解释了。
List&Integer& ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println(&ints sum is:& + ints.stream().count());
– 搜索相关
– allMatch:是不是Stream中的所有元素都满足给定的匹配条件
– anyMatch:Stream中是否存在任何一个元素满足匹配条件
– findFirst: 返回Stream中的第一个元素,如果Stream为空,返回空Optional
– noneMatch:是不是Stream中的所有元素都不满足给定的匹配条件
– max和min:使用给定的比较器(Operator),返回Stream中的最大|最小值
下面给出allMatch和max的例子,剩下的方法读者当成练习。
List&Integer& ints = Lists.newArrayList(1,2,3,4,5,6,7,8,9,10);
System.out.println(ints.stream().allMatch(item -& item & 100));
ints.stream().max((o1, o2) -& o1.compareTo(o2)).ifPresent(System.out::println);
本文已收录于以下专栏:
相关文章推荐
本文主要介绍了Java8 新api Stream的使用案例。实现排序,去重,对比,筛选,分组,收集,聚集等等功能。主要包含sort()函数,distinct()函数,map()函数,collect()...
java中list里面存放map,根据map中的某一个字段进行排序
import java.util.ArrayL
import java.util.Co...
    Stream的优点:声明性,可复合,可并行。这三个特性使得stream操作更简洁,更灵活,更高效。
    Stream的操作有两个特点:可以多个操作链接起来运行;内部迭代;
  ...
public static T& PredicateT& distinctByKey(Functionsuper T, ?& keyExtractor) {
Map, Boolean& see...
一、去除List中重复的Stringpublic List removeStringListDupli(List stringList) {
Set set = new LinkedHashS...
1. Stream初体验
我们先来看看Java里面是怎么定义Stream的:
A sequence of elements supporting sequential and para...
通过前面的博客的介绍,我们知道Stream有一个源,0个或者多个中间操作,以及一个终止操作。Stream只有遇到终止操作,它的源才开始执行遍历操作,而且只会进行一次遍历,而不是每个操作都执行一次遍历。...
欢迎进入我的博客:查看更多文章
stream作为Java8的一个重要的新特性,其提供了对于集合的流处理模式,引出了管道的概念,即通过获取stream流,之后可根...
在Java 8中对stream带有lamda表达式的操作进行调试通常在开发程序时候都会对程序进行单步或设置断点调试代码。而在Java 8中在使用stream时采用这种方式调试却与原有的方式稍有不同。 ...
1.从迭代器到Stream操作以前我们遍历一个集合可以用for 循环,iterator迭代器。jdk8新推出了一项特性,Stream语法,用Stream语法可以让代码更加简洁,而且原生支持的并发操作也...
他的最新文章
讲师:姜飞俊
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 java stream reduce 的文章

 

随机推荐