java8 combat: the use of collecting data stream toList, joining, groupBy (multi-field packet)

java8 column directory:

  1. java8 real study notes: Lambda expression syntax and functional programming interfaces
  2. java8 real study notes: Composite Lambda Expressions
  3. java8 actual reading notes: acquaintance Stream, the basic operation flow (flow calculation)
  4. java8 combat Reading Notes: use value stream, Stream creates Optional classes
  5. java8 study notes: Explore the collection of data flow principle java8

This paper will Collectos constructed collectors start, which provides detailed java8 collector, focusing on: toList, toSet, toCollection, joining, groupBy (with multiple levels of grouping), reducing the core principles and implement the use of examples.

Operation collections

Collections operations include toList, toSet, toCollection. First, the data in the stream is calculated, the data type returned by the final set. Collectors are defined as collections collector 3, which states:

1public static <T> Collector<T, ?, List<T>> toList()
2public static <T> Collector<T, ?, Set<T>> toSet()
3public static <T, C extends Collection<T>> Collector<T, ?, C> toCollection(Supplier<C> collectionFactory)

Tips: article recommendations based on the theory of anti-again push the value of the core attributes of these Collector, such supplier, accumulator, combiner, characteristics. But special attention, toList, toCollection is not supported running in parallel, but toSet () method supports parallel operation.

We first look at an example has been used to return the names of all the dishes in the menu:

1public static void test_toList(List<Dish> menu) {
2    List<String> names = menu.stream().map(Dish::getName)
3                        .collect(Collectors.toList());
4}

As the realization of the principle toList methods have been reading notes java8: explore java8 flow data collection principles are also described in detail, it is no longer Benpian highlights.

joining

Collectors defines the following three overloaded methods.

1public static Collector<CharSequence, ?, String> joining()
2public static Collector<CharSequence, ?, String> joining(CharSequence delimiter)
3public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
4    CharSequence prefix, CharSequence suffix)

2.1 joining

1public static Collector<CharSequence, ?, String> joining() {
2    return new CollectorImpl<CharSequence, StringBuilder, String>(
3        StringBuilder::new, StringBuilder::append,
4        (r1, r2) -> { r1.append(r2); return r1; },
5        StringBuilder::toString, CH_NOID);
6}
  • Supplier <A> supplier () is a function of StringBuilder :: new, i.e. StringBuilder create a method which, as the initial value of the accumulator.
  • BiConsumer accumulator accumulator: StringBuilder :: append, i.e. stream elements will additionally performed.
  • BinaryOperator <A> combiner combiner, append method is invoked, the string for the statute.
  • Function finisher Converter: Since the final target cumulative returns for StringBuilder, not the target of type String, so call the StringBuilder # toString method to convert
  • Set <Characteristics> characteristics without any action.

From the function defined above we can conclude that the role of the method: a string for the stream, additional character element will perform the operation flow, there is no delimiter between the flow elements, for example:

2.2 joining(CharSequence delimiter)

 1public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) {
 2    return joining(delimiter, "", "");
 3}
 4public static Collector<CharSequence, ?, String> joining(CharSequence delimiter,
 5                                                         CharSequence prefix,
 6                                                         CharSequence suffix) {
 7    return new CollectorImpl<>(
 8            () -> new StringJoiner(delimiter, prefix, suffix),
 9            StringJoiner::add, StringJoiner::merge,
10            StringJoiner::toString, CH_NOID);
11}
  • Supplier <A> supplier () is a function of () -> new StringJoiner (delimiter, prefix, suffix), the initial value StringJoiner accumulator.
  • BiConsumer accumulator accumulator: StringJoiner :: append, i.e. stream elements will additionally performed.
  • BinaryOperator <A> combiner combiner, StringJoiner :: merge.
  • Function finisher Converter: Since the final target cumulative returns for StringBuilder, not the target of type String, so call the StringBuilder # toString method to convert
  • Set <Characteristics> characteristics without any action.

Examples of which are as follows:

Related polymerization collector

Related collector polymerization, including minBy, maxBy, sum, avg, and other related functions, the main method of the following statement:

1public static <T> Collector<T, ?, Optional<T>> minBy(Comparator<? super T> comparator)
2public static <T> Collector<T, ?, Optional<T>> maxBy(Comparator<? super T> comparator)
3public static <T> Collector<T, ?, Integer> summingInt(ToIntFunction<? super T> mapper)
4public static <T> Collector<T, ?, Long> summingLong(ToLongFunction<? super T> mapper)
5public static <T> Collector<T, ?, Double> summingDouble(ToDoubleFunction<? super T> mapper)
6public static <T> Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper)
7public static <T> Collector<T, ?, Double> averagingLong(ToLongFunction<? super T> mapper)
8public static <T> Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> mapper)

上面这些方法比较简单,下面举个简单的例子介绍其使用:

分组

Collectors提供了3个groupingBy重载方法,我们一个一个来理解。

4.1 从示例入手

我们从其中一个最简单的函数说起,从而慢慢引出

1public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(
2             Function<? super T, ? extends K> classifier)
  • Collector>> 首先我们先来关注该方法的返回值Collector<T, ?, Map<K,List< T>>,其最终返回的数据类型为:Map<K, List< T >>
  • Function classifier 分类函数。

示例如下:例如如下是购物车实体类,并且初始化数据如下:

 1public class ShopCar {
 2    private int id;
 3    private int sellerId;
 4    private String sellerName;
 5    private String goodsName;
 6    private int buyerId;
 7    private String buyerName;
 8    private int num;
 9}
10// 初始化数据如下:
11public static List<ShopCar> initShopCar() {
12    return Arrays.asList(
13            new ShopCar(1, 1, "天猫" , "华为手机", 1 , "dingw", 5),
14            new ShopCar(1, 2, "京东" , "华为手机", 2 , "ly", 2),
15            new ShopCar(1, 1, "京东" , "小米手机", 3 , "zhl", 3),
16            new ShopCar(1, 2, "1号店" , "华为手机", 1 , "dingw", 5),
17            new ShopCar(1, 2, "天猫" , "苹果手机", 1 , "dingw", 2)
18    );
19}

首先我们看一下java8之前的写法:

 1public static void test_group_jdk7(List<ShopCar> shopCars) {
 2    Map<String, List<ShopCar>> shopBySellerNameMap = new HashMap<>();
 3    for(ShopCar c : shopCars ) {
 4        if(shopBySellerNameMap.containsKey( c.getSellerName() )) {
 5            shopBySellerNameMap.get(c.getSellerName()).add(c);
 6        } else {
 7            List<ShopCar> aList = new ArrayList<>();
 8            shopBySellerNameMap.put(c.getSellerName(), aList);
 9            aList.add(c);
10        }
11    }
12    print(shopBySellerNameMap);
13}

上面的代码应该很容易理解,根据商家名称进行分组,拥有相同商家的名称的购物车项组成一个集合,最终返回Map>类型的数据。

那如何使用java8的流分组特性来编写对应的代码呢?下面的思考过程非常关键,经过前面的学习,我想大家应该也具备了如下分析与编写的能力?

首先其声明如下:public static Collector>> groupingBy(Function classifier),那在本例中,T,K这两个参数代表什么意思呢?

  • T : ShopCar
  • K : String (sellerName的类型) 其判断的主要依据为groupingBy方法返回的参数Collector< T, ?, Map< K, List< T>>>,代表< T, A, R>,其中最后一个泛型参数R对应的就是本例需要返回的Map< K, List< T>>,故分析出T,K代表的含义。

然后再看其参数:Function classifier,即接受的函数式编程接口为T -> K,即通过ShopCar 返回一个String,又根据其名称可知,该函数为一个分类函数,故基本可以写成如下代码:

1public static void test_group_jdk8(List<ShopCar> shopCars) {
2    Map<String, List<ShopCar>> shopBySellerNameMap =  
3                 shopCars
4                     .stream()
5                     .collect(Collectors.groupingBy(ShopCar::getSellerName));
6                   //.collect(Collectors.groupingBy( (ShopCar c) -> c.getSellerName() ))
7    print(shopBySellerNameMap);
8}

其运行效果如下:

为了加深对groupingBy方法的理解,接下来我们重点分析一下其源码的实现。

4.2 源码分析groupingBy方法

1public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier) {  // @1
2    return groupingBy(classifier, toList());                                                                     // @2
3}

代码@1:分类参数,已经在上文中详细介绍。

代码@2:调用groupingBy重载方法,传入的参数为toList(),有点意思,传入的参数为Collectors.toList(),结合上文中的示例,需要返回值类为:Map< String, List< ShopCar>>,与这里的List对应起来了。

1public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) {
2    return groupingBy(classifier, HashMap::new, downstream);
3}
该重载方法,再次调用3个参数的groupingBy方法。
 1public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(
 2                          Function<? super T, ? extends K> classifier, 
 3Supplier<M> mapFactory,
 4Collector<? super T, A, D> downstream) { // @1
 5    Supplier<A> downstreamSupplier = downstream.supplier();        // @2 start
 6    BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();
 7    BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {
 8        K key = Objects.requireNonNull(classifier.apply(t), "element cannot be mapped to a null key");
 9        A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());
10        downstreamAccumulator.accept(container, t);
11    }; // @2 end
12
13    BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner());   // @3
14    @SuppressWarnings("unchecked")
15    Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory;                            
16
17    if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {           // @4
18        return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);
19    }
20    else {                                                                                            // @5
21        @SuppressWarnings("unchecked")
22        Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();
23        Function<Map<K, A>, M> finisher = intermediate -> {
24            intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));
25            @SuppressWarnings("unchecked")
26            M castResult = (M) intermediate;
27            return castResult;
28        };
29        return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);
30    }
31}

代码@1:参数介绍:

  • Function classifier 分类函数。
  • Supplier< M> mapFactory map构建函数。()-> Map
  • Collector downstream 下游收集器,在上面的示例中,该参数为Collectos.toList()。

代码@2:构建最终的累积器。其实现要点如下:

  • 对流中的元素,使用Function classifier,获取对应的分类键值。
  • 使用mangledFactory创建累积初始值,并调用Map#computeIfAbsent方法,放入的值为:downstreamSupplier.get()。可以类比上例中Map>,请结合如下代码进行理解:

代码@3:构建最终的组合器,这里使用的是Collectos.mapMerger,其内部的实现就是对每个元素,执行map#merge方法。

代码@4:如果收集器的行为为IDENTITY_FINISH,直接根据上面已创建的累积器、组合器,创建一个最终的收集器。

代码@5:如果收集器的行为不包含IDENTITY_FINISH,则需要最终调用原收集器的finisher方法。才能最终需要返回的类型。

groupingBy的原理就讲解到这里,我们接下来思考如下场景: 还是上面的购物车场景,现在要求先按照供应商名称分组,然后按照购买人分组(即多级分组),类似于SQL group by sellerId,buyerId。

思考过程:首先二级分类需要返回的数据类型为Map> >,而只有一个参数的groupingBy(Function classifier),只接受一个分类参数,其内部会调用两个参数的groupingBy(Function classifier,Collector downstream),默认第二个参数为Collectors.toList(),故我们可以做的文章是改变这个默认值,传入符合业务场景的收集器,结合目前的需求,很显然,该参数应该是支持分组的收集器,即应该可以通过嵌套groupingBy方法,实现二级分组,其具体代码如下:

 1/**
 2 * 二级分组示例
 3 * @param shopCars
 4 */
 5public static void test_level_group(List<ShopCar> shopCars) {
 6    Map<String, Map<String, List<ShopCar>>>  result = 
 7        shopCars.stream().collect(Collectors.groupingBy(ShopCar::getSellerName,
 8                                    Collectors.groupingBy(ShopCar::getBuyerName)));
 9    System.out.println(result);
10}

温馨提示:上面介绍的分组,主要的Map存储结构为HashMap,java8为ConcurrentMap对应类继承体系提供了对应的分组函数:groupingByConcurrent,其使用方法与groupingBy方法类型,故不重复介绍。

partitioningBy

分区,分区可以看出是分组的特殊化,接受的分类函数返回boolean类型,即是谓词Predicate predicate。其声明如下:

1public static <T> Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate)
2public static <T, D, A> Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T, A, D> downstream) 

由于其用法与分组类似,故这里就一笔带过了。

reducing

规约。其函数声明如下:

1public static <T, U> Collector<T, ?, U> reducing(U identity, Function<? super T, ? extends U> mapper, BinaryOperator<U> op)

其参数如下:

本文分享自微信公众号 - 中间件兴趣圈(dingwpmz_zjj)

原文出处及转载信息见文内详细说明,如有侵权,请联系 [email protected] 删除。

原始发表时间:2019-06-10

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

举报

点赞 4分享

我来说两句

0 条评论

登录 后参与评论

推荐阅读

  • 远程办公经验为0,如何将日常工作平滑过度到线上?

    我是一名创业者,我的公司(深圳市友浩达科技有限公司)在2018年8月8日开始运营,现在还属于微型公司。这个春节假期,我一直十分关注疫情动向,也非常关心其对公司带来的影响。

    TVP官方团队 11 天前

    TAPD 敏捷项目管理腾讯乐享企业邮箱企业编程算法

  • 数据中台,概念炒作还是另有奇效? | TVP思享

    作者简介:史凯,花名凯哥,腾讯云最具价值专家TVP,ThoughtWorks数据智能业务总经理。投身于企业数字化转型工作近20年。2000年初,在IBM 研发企业级中间件,接着加入埃森哲,为大型企业提供信息化架构规划,设计,ERP,云平台,数据仓库构建等技术咨询实施服务,随后在EMC负责企业应用转型业务,为企业提供云迁移,应用现代化服务。现在专注于企业智能化转型领域,是数据驱动的数字化转型的行业布道者,数据中台的推广者,精益数据创新体系的创始人,2019年荣获全球Data IQ 100人的数据赋能者称号,创业邦卓越生态聚合赋能官TOP 5。2019年度数字化转型专家奖。打造了行业第一个数据创新的数字化转型卡牌和工作坊。创建了精益数据创新方法论体系构建数据驱动的智能企业,并在多个企业验证成功,正在向国内外推广。

    TVP官方团队 11 天前

    大数据数据分析企业

  • 扩展 Kubernetes 之 CRI

    使用 cri-containerd 的调用流程更为简洁, 省去了上面的调用流程的 1,2 两步

    王磊-AI基础 28 天前

    Kubernetes

  • 扩展 Kubernetes 之 Kubectl Plugin

    kubectl 功能非常强大, 常见的命令使用方式可以参考 kubectl --help,或者这篇文章

    王磊-AI基础 26 天前

    Kubernetes

  • 多种登录方式定量性能测试方案

    最近接到到一个测试任务,某服务提供了两种登录方式:1、账号密码登录;2、手机号+验证码登录。要对这两种登录按照一定的比例进行压测。

    八音弦 14 天前

    测试服务 WeTest

  • 线程安全类在性能测试中应用

    首先验证接口参数签名是否正确,然后加锁去判断订单信息和状态,处理用户增添VIP时间事务,成功之后释放锁。锁是针对用户和订单的分布式锁,使用方案是用的redis。

    八音弦 14 天前

    安全编程算法

  • 使用CDN(jsdelivr) 优化博客访问速度

    PS: 此篇文章适用于 使用 Github pages 或者 coding pages 的朋友,其他博客也类似.

    IFONLY@CUIT 15 天前

    CDNGitGitHub开源

  • 扩展 Kubernetes 之 CNI

    Network Configuration 是 CNI 输入参数中最重要当部分, 可以存储在磁盘上

    王磊-AI基础 29 天前

    Kubernetes

  • 聚焦【技术应变力】云加社区沙龙online重磅上线!

    云加社区结合特殊时期热点,挑选备受关注的音视频流量暴增、线下业务快速转线上、紧急上线防疫IoT应用等话题,邀请众多业界专家,为大家提供连续十一天的干货分享。从视野、预判、应对等多角度,帮助大家全面提升「技术应变力」!

    腾小云

发布了51 篇原创文章 · 获赞 82 · 访问量 95万+

Guess you like

Origin blog.csdn.net/xiyang_1990/article/details/104631532