归约、分组与分区,深入讲解JavaStream终结操作

本文为社区首发签约文章,未获授权制止转载。

归约、分组与分区,深化解说JavaStream完毕操作

思维导图镇楼,先感谢咱们对我上一篇文的积极点赞,助我完毕KPI。

上一篇中给咱们讲了Stream的前半部分知识——包括对Stream的整体概览及Stream的创建和Stream的转换流操作,并对S函数式编程言语排行榜tream一些内部优化点做了简明的阐明。

虽迟但到,今天就来持续给咱们更Stream第二部分知识—数据处理办法有哪些—完毕操作,因为这部分的API内容繁多且杂乱,所以我单开一篇给咱们细细讲讲,我的文章很长,请咱们忍受一下。

正式开端之前,咱们先来说说聚合办法自身的特性(接下来我将用聚合办法代指完毕操作中的函数式编程入门办法):

  1. 聚合办法代表着整接口和抽象类的差异个流核算的最完毕果,所以它的回来值都不是Stream。

  2. 聚合办法回来值或许为空,比方filter没有匹配到的状况,JDK8顶用Optional来规避NPE。

  3. 聚合办法都会调用evaluate办法,这是一个数据处理是什么内部办法,看函数式编程 java源码的进appear程中能够用它来断定一个办法是不是聚合办法。

ok,知晓了聚合方接口测验面试题法的特性,我为了便于了解,又将聚合方数据处理是什么作业法分为几大类:

归约、分组与分区,深化解说JavaStream完毕操作

其间简略聚合办法我会简略阐明,其它则会着重阐明,尤其是搜集器,它能做的实在太多了。。。

Stream的聚合办法是咱们在运用Stream中的必用操作,认真学习appreciate本篇,不说立刻就能对Stream称心如意,最少也能够行云流水apple

归约、分组与分区,深化解说JavaStream完毕操作

1. 简略聚合办法

第一节嘛,先来点简略的。

Stream的聚合办法比上一篇讲过的无状况和有状况办法都要多函数式编程 java,可是其间也有一些是喵一眼就能学数据处理的常用办法有哪些会的,第一节咱们先来说说这部分办法:

  • count():回来Stream中元素的size巨细。

  • forEach():经过内部循环Stream中的悉数元素,对每一个元素进行消费,此办法没有回来值。

  • forEachOrder():和上面办法的效果相同,但多线程面试题是这个能够坚持消费次第,哪怕是在多线程环境下。

  • anyMatch(Predicate predicate):这是一个短路操作,经过传入断语参数判别是否有元素能够匹配上断语。

  • allMatch(Predicate predica多线程cpu有什么优点te):这是一个短路操作,经过传入断语参数回来是否悉数元素都能匹配上断语。

  • noneMatch(Predicate predicate):这是一个短路操作,经过传入函数式编程言语排行榜断语参数判别是否悉数元素都无法匹配上断语,假定是则回来true,反之则false。

  • findFirst():这是一个短路操作,回来Stream中的第一个元素,Stream或许为空所以回来值用Optional处理。

  • findAny():这是一个短路操作,回来Stream中的任意一个元素,串型流中一般是第一个元素,Stream或许为空所以回来值用Op数据处理是什么tional处理。

尽管以上都比较简略,但数据处理软件是这儿面有五个涉及到短路操作的办法我仍是想提两嘴:

首要是findFirs多线程形式怎样开t()findAny()这两个办法, 因为它们数据处理技能的开展阅历了三个阶段只需求拿到一个元素就能办法就能完毕,所以多线程短路效果很好了解。

接着是anyMatch办法,它只需求匹配到一个元素办法也能完毕,所以它的短路效果也很好了解。

究竟是allMatch办法和noneMatch,乍一看这两个办法都是需求遍历整个流中的悉数元素的,其实否则,比方allMatch只需有一个元素不匹配断语它就可接口的效果以回来false了,noneMa数据处理软件tch只需有一个元素匹配上断语它也能够回来false了,所以它们都是具有短路效果的办法。

2. 归约

2.1 reduce:重复求值

第二节咱们来说说归约app下载,因为这个词过于笼统,我不得不找了一句通俗易懂的阐明来翻译这句话,下面是归约的界说:

将一个Stream函数式编程中的悉数元素重复结合起来,得到一个效果,这样的操作被称为归约接口

注:在函数式编程中,这叫做接口crc过错计数折叠( fold )函数式编程言语

举个很简略的比方,我有1、2、3三个元素,我把它们俩俩相加,究竟得出6这个数字,这个进程便是归约。

再比方数据处理和剖析的进程,我有1、2、3三个元素,我把它们俩俩比较,究竟挑出最大的数字3或许挑出最小的数字1,这个进程也是归约。

下面我举一个求和的比方来演示归约,归约运用reduce办法:

        Optional<Integer> reduce = List.of(1, 2, 3).stream多线程的完成办法()
.reduce((i1, i2) -> i1 + i2);

首要你或许留神到了,我在上文的小比方中一数据处理和剖析的进程直在用俩俩这个词,这代表归约是俩俩的元素进行函数式编程思维处理然后得到一个究竟值,所以reduce的办法的参数是一个二元表数据处理是什么作业达式,它将两个参数进行任意处接口的效果理,究竟得到一个效果,其间它的参数和效果有必要是同一类型。

比方代码中的,i1和i2便是二元表达式的两个参数,它们别离代表元素中的第一个元素和第二个元素,当第一次相加完毕后,所得的效果会赋值到i1身上,i2则会持续代表下一个元素,直至元素耗尽,得到最完毕果。

假定你觉得这么写不行典雅,也能够运用Integer中的默许办法:

        Optional<Integer> reduce = List.of(1, 2, 3).stream函数式编程 java()
.r数据处理的常用办法有哪些educe(Integer::sum);

这也是一个以办法引用代表lambda表达式的比方。

你或许还留神到了,它们的回来函数式编程值是接口crc过错计数Optionaapplel的,这是防备Stream没有元素的状况。多线程编程

你也能够想办法去掉这种状况,那便是让元素中至少要接口有一个值,这儿reduce供给一个重载办法给咱们:

        Integer red接口类型uce = List.of(1, 2, 3).stream()
.reduce(0, (i1, i2) -> i1 + i2);

如上例,在二元表达式前面多接口crc过错计数加了一个参数,这个参数被称为初始值,这样哪数据处理技能的开展阅历了三个阶段怕你的Stream没有元素它究竟也会回来一个0,这样就不需求Optiona函数式编程言语l了。

在实践办法运行中,初始值会在第一次实施中占有i1的方位,i2则代表Stream中的第APP一个元素,然后所得的和再次占有i1的方位,i2代表下一个元素。

不过运用多线程的完成办法初始值不是没有本钱的,它应该符合一个准则:accumulator.apply(ide多线程形式怎样开ntit接口的效果y, i1) == i1函数式编程,也便是说在第一次实施的时分,它的回来效果多线程并发都应该是你Stream中接口测验的第一个元素。

比方我上接口文档面的比方是一个相加操作,则第一次相加时便是0 + 1 = 1,符合上面的准则,作此准则是为了确保并行流状况下能够得到正确的效果。

假定你的初始值是1,则在并发状况下每个线程的初始化都是1,那么你的究竟和就会比你意料的效果要大。

2.2 max:运用归约求最大数据处理技能的开展阅历了三个阶段

max办法也是一个归约办法,它是直接调用了reduce办法。

先来看一个示例数据处理是什么作业

        Optional<Integer&接口文档gt; max = List.of(1, 2, 3).stream()
.max((a, b) -> {
if (a > b) {
return 1;
} else多线程形式怎样开 {
return -1;
}
});

没错,apple这就appstore是max办法用法,这让我觉得我不是在运用函数式接口,当然你也能够运用Integer的办法进行简化:

        Optional<Integer&applicationgt; max多线程和多进程的差异 = List.of(1, 2, 3).stream()
.max(Integer::compare);

哪怕如此,这个办法仍旧让我感觉到很繁琐,我尽管能够了解在max办法里边传参数是为了让咱们自己自界说排序规矩,但我不了接口的效果解为什么没有一个默许依照天然排序进行排序的办法,而是非要让我传参数。接口测验

直到后来我想到了基础类型Stream,公开,它们里边是能够无需传参直接拿到最大值:

        OptionalLong max数据处理的最小单位 = LongStream.of(1, 2, 3).max()接口卡;

公开,我能想到的,类库设计者都想到了~

:OptionalLong是Optional对基础类型long的封装。

2.3 min:运用归约求最小

min仍是直接看比方吧:

        Optional<Integer> max = List.of(1, 2, 3).stream()
.min(Integer::compare);

它和max差异便是底层把 > 换成了 <,过于简略,不再赘述。

3. 搜集器

第三节咱们来看看搜集器,它的效果是对Stream中的元素进行搜集而构成一个新的集结。

尽管我在本篇开端的时分现已给过一张思维导图了,可是因为数据处理的常用办法有哪些搜集器的API比较多所以我又画了一张,算是对开端那张的补充:

归约、分组与分区,深化解说JavaStream完毕操作

搜集器的办法名是collect,它的办法界说如下:

    <R, A> R collect(Collector<? super T, A, R> collector);

断章取义,收接口类型集器是用来搜集Strea多线程编程m的元素的,究竟搜集成什么咱们能够自界说,可是咱们一般不需求自己写,因为JDK内置了一个Colle函数式编程言语排行榜ctor的完毕类——Collectors。

3.1 搜集办法

经过Co多线程并发llectors咱们能够运用它的内置办法很便利的进行数据收数据处理软件集:

比方你想把元素搜集成集结,那么你能够运用toCollection或许toList办法,不过咱们一般不运用toCollection,因为它需求传参数,没人喜爱传参数。

你也能够运用toUnmodifiableList,它和toList差异便是它回来接口的集结不能够改动元素,比方删去或许新增。

再比方你要把元素去重之后搜集起来,数据处理技能的开展阅历了三个阶段那么你能够运用toSet或许toUnmodifiableSet。

接下来放一个比较简略appstore的比方:

        // toList
List.of(1, 2, 3).stream()数据处理是什么作业.coll接口的效果ect(Collectors.toList());
// toUnmodifiableList
List.of(1, 2, 3).stream().collect(Collectors.toUnmodifiableList()数据处理软件);
// toSet
List.of(1, 2, 3).多线程的完成办法stream().collect(Collectors函数式编程.toSet());
// toUnmodifiableSet
List.of(1, 2, 3).stream().collect(Collectors.toUnmodifiableSet());

以上这些办法都没有参数,拿来即用,toList底层也是经典的ArrayList,toSet数据处理的最小单位 底层则是经典的H数据处理是什么ashSet。


或许有时分你或许想要一个搜集成一个Map,比方经过将订单数据转成一个订单号对应一个订单,那么你能够运用toMap():

        List<Order> orders = List.of(new Order(), new Order());
M数据处理的常用办法有哪些ap<String, Order> map = orders.stream()
.c多线程cpu有什么优点ollect(Collectors.app下载toMap(Order::getOr数据处理的常用办法有哪些derNo, or函数式编程和面向对象的差异der -> order));

tAPPoMap() 具有两个参数:

  1. 第一个参数代表key,它标明你要设置一个Map的key,我这儿指定的是元素中的orderNo。

  2. 第二个参数代表value,它标明你要设置一个Map的value,我这儿直接把元素自身接口是什么当作值,所以效果是一个Map<String, Order>。

你也能够将元素的特征当作值数据处理的常用办法有哪些

        List<接口的效果Order> orders =接口测验 List.of(new Order函数式编程言语(), new Order())多线程cpu有什么优点;
Map<String, List<Item>> map = orders.stream()
.co多线程编程llec接口crc过错计数t(Collectors.toMap(Order::getOrderNo, Order::getItemList));

这样回来的便是一个订单号+产品列表的Map了。

toMap() 还有两个伴生办法:

  • toUnmodifiableMap():回来一个不行批改的Map。

  • toConcurrentMap():回来一个线程安全的Map。

这两个办法和toMap() 的参数一模相同,仅有不同的便是底层生成的Map特性不太相同,咱们一般运用简简略单的toMap() 就够了,它的底层是咱们最常用的HashMap() 完毕。

toMap() 功用尽管强壮也很常用,可是它接口crc过错计数却有一个致命缺点。

咱们知道HahsMap接口卡遇到相同的key会进行掩盖操作,可是to接口类型Map() 办法生成Map时假定你指定的key呈现了重复,那么它会直接抛出失常。多线程编程

比方上面的订单比方中,我接口卡们假定两个订单的订单号相同,可是你又将订单号指定了为key,那么该办法会直接抛出一个IllegalStateExcept多线程形式怎样开ion,因为它不答应元素中的keapproachy是相同的。

3.2 分组办法

假定你想对数据进行分类,可是你指定的key是能够重复的,那么你应该运用groupingBy 而不是toMap。

举个简略的比方,我想对一个订单集结以订单类型进行分组,那么appointment能够这样:

        List<Order> orders = List.of(new Order(), ne多线程使用场景比方w Order());
Map<Integer, List<Order>> collect = orders.st数据处理的常用办法有哪些ream()
.collect(Collectors.groupingBy(Order::getOrderType));

直接指定用于分组的元素特征,它就会自动依照此特征进行分组,并将分组的效果搜集为一个List。

        List<Order> orders = List.of(new O接口自动化rder(), new Order());
Map<Integer, Set<Order>> collect = orders.stream()
.collect(Co接口卡llectors.groupingBy(Order::getOrderType, toSet()));

groupingBy还供给了一个重载,让你能够自界说搜集器类型,所以它的第函数式编程 java二个参数是一个Collector搜集器方针。

关于Co多线程下载llector类型,咱们一般仍是运用Collectors类,这儿因为咱们前面现已运用了Collectors,所以这儿不必声明直接传入一个toSet()办法,代表咱们将分组后的元素搜集为Set。

groupingBy还有一个类似的办法叫做groupi接口和抽象类的差异ngByConcurrent(),这个办法能够在并行时进步分组功率,可是它是不确保次第的,这儿就不打开讲了。

3.3 分区办法

接下来我将介绍分组的另一种状况——分区,名函数式编程js字有点绕,但意思很简略:

将数据依照TRUE或许FALSE进行分组就叫数据处理做分区。

举个比方,咱们将一个订单集结依照是否支数据处理技能的开展阅历了三个阶段付进行分组,这便是分区:

        List<Order> orderappears = List.of(new Order(), new Order());
Map&lt函数式编程和面向对象的差异;Boolean, Li多线程下载st<appreciateOrder>> collect = orders.stream()
.collect(Collectors.函数式编程partitioningBy(Ord多线程编程er::getIsP接口测验aid));

因为订单是否支付只具有两种状况:已支付和未支付,这种分组办法咱们就叫做分区。

和groupingBy相同,它还具有函数式编程入门一个重载方apple法,用来自界说搜集器类型:

        List<Order> orders = List.of(new Order(), new Order());
Map<Boolean, Set<Order>> collect = orders.stream()
.collect(Collec数据处理是什么作业tors.partitioningBy(Or多线程下载der::getIsPaid, toSet()));

3.4 经典复刻办法

总算来到究竟一节了,请原谅我给这部分的办法起了一个这么土的姓名,可是这些办法确实如我所说:经典复刻。

换言之,便是Collectors把Stream原先的办法又完毕了一遍,包括:

  1. mapmapping

  2. fil数据处理员是干什么的terfiltering

  3. flatMapflatMapping

  4. countcounting

  5. reducereducing

  6. maxmaxBy

  7. **min ** → minBy接口

这些办法的功用我就不逐个列举了,之前的文章现已讲的很详尽了,仅有的不同是某些办法多了一个参数,这个参数便是我接口测验们在分组和分区里边讲过的搜集参数,你能够指定收函数式编程的优点集到什么容器内。

我把它们抽出来首要想说的为多线程编程什么要复刻这么多办法处理,这儿我说说个人见解,不代表官方定见。

我觉得首要是为了功用的组合。

什么意思呢?比方说我又有一个需求:运用订单类型对订单进行分组,并找出每组有多少个订单。

订单分组咱们现已讲过了,找到其每组有多少订单只需拿到对应list的size就行了,可是我接口crc过错计数们能够不接口这么费事,而是一步到位,在输出效果的时分键值对便是订单类型和订单数量:

        Map<Integer, Long> collect = orders.stream()
.collect(Collect数据处理办法有哪些ors.groupingBy(Order::getOrderType, counting()));

就这样,就这么简略,就好了,这儿等于说咱们对分组后的数据又进行了一次计数操作。

上面的这个比方或许不对显着,当我接口们需求对究竟搜集之后的数据在进行操作时,一般咱们需求从头将其转换成Stream然后操作,可是运用Collectors的这些办法就能够让你很便利的在Collectors中进行数据的处理。数据处理软件

再举个比方,仍是经过订单类型函数式编程思维对订单进行分组,可是呢,咱们想要拿到每种类型订单金额最大的那个,那么咱们就能够这样:

        List<Order> orders = List.of(new Order(), new Order())函数式编程;
Map<Integer, Optional<Order>> collect2 = orders.stream()多线程是什么意思
.collect(groupingBy(Order::getOrderType,
maxBy(Comparator.comparing(Order::getMoney接口crc过错计数))));

更简练,也更便利,不需求咱们分组完接口的效果之后再去逐个寻觅最大值了,能够一步到位。

再来一个分组之后,求各组订单金额之后的:

        List<Order> orders = List.of(new Order(), new Order());
Map函数式编程 java<Integer,approach Long> collect = orders.stream()
.collect(groupingBy(Ord函数式编程言语排行榜er::getOrderType, summingLong(Order::getMoney)));

不过sum函数式编程mingLong这儿咱们没有讲,它便是一个内置的请和操作,支撑Integer、Long和Double。

还有一个类似的办法叫做averagingL函数式编程的优点on接口是什么g看姓名就知道,求平均的,都比较简略,主张咱们没事的时分能够扫两眼。


该完毕了,究竟一个办法joi函数式编程思维ning(),用来拼接字符串很有用:

        List<Order> orders =数据处理和剖析的进程 List.of(new Order(), new Order());
String collect = orders.stream()
.map(Order::getOrd数据处理员erNo).collect(Collectors.joi数据处理是什么ning(","));

这个办法的办法名看着appear有点眼熟,没错,String类在JDK8之后新加了一个join() 办法,也是用来拼接字符串的,Colleapplectors的joining不过和多线程使用场景比方它功用相同,底层完毕也相同,都用了S函数式编程思维tringJoi接口crc过错计数ner类。

4. 总结

总算接口和抽象类的差异写完了。

在这篇多线程下载Stream中完毕操作中,我提了Stream中的悉数聚合办法,能够函数式编程说你看完了这篇,Stream的悉数聚合操作就掌握个七七八八了,不会用没关系,就知道有这个东西了就行了,否则在你的知识系统中St函数式编程 javaream底子做不了XX事,就有点见笑大方了。

当然,我仍是主张咱们多线程面试题在项目中多多用用这些简练的API,进步代接口自动化码可读性,也愈加简练,被review的时分函数式编程言语排行榜也简单让他人眼前一亮~

看到这的掘友,期望高抬贵手帮我点个赞,为我的KPappearI大业出一份力,你们的支撑便是我发明的不竭动力,咱们下期见。


参阅书本:

  • Java8实战

推荐文章:

  • 延迟实施与不行变,多线程是什么意思系统阐明JavaStream数据处理

发表评论

提供最优质的资源集合

立即查看 了解详情