Collection stream使用示例详解

目录
  • 基础数据
  • 元素转Stream
  • Terminal opt-Collectors.mapping
  • Terminal opt-Collectors.toCollection&collectingAndThen
  • Terminal opt-Collectors.toMap

近日频繁应用 Stream 的 Api,记录一下应用实例。

基础数据

实体类:

@Data
@Accessors(chain = true)
public static class Person {
    private String name;
    private Integer age;
    private String hobby;
    private LocalDateTime birthday;
}

数据:

public List<Person> data() {
    List<Person> data = new ArrayList<>();
    data.add(new Person().setName("张三").setAge(25).setHobby("LOL,Food").setBirthday(LocalDateTime.of(1997, 1, 1, 0, 0)));
    data.add(new Person().setName("李四").setAge(25).setHobby("CS:GO,Swimming").setBirthday(LocalDateTime.of(1997, 2, 1, 0, 0)));
    data.add(new Person().setName("王五").setAge(30).setHobby("RedAlert2,Beer").setBirthday(LocalDateTime.of(1992, 3, 1, 0, 0)));
    data.add(new Person().setName("赵六").setAge(40).setHobby("War3,Journey").setBirthday(LocalDateTime.of(1982, 4, 1, 0, 0)));
    data.add(new Person().setName("孙七").setAge(40).setHobby("DOTA,Jogging").setBirthday(LocalDateTime.of(1982, 5, 1, 0, 0)));
    return data;
}

元素转Stream

当我们需要将一个单值元素转转为一个多值元素,并进行统一收集,flatMap在适合不过。

flatMap 函数:将单个元素映射为Stream

参数:

Function mapper:元素映射为 Stream 的过程。

栗子:

/**
 * 获取所有成员的爱好集合
 */
@Test
public void test1() {
    // 方式1:
    // 先进行 map 映射单个元素为多值元素
    // 在将多值元素映射为 Stream
    Set<String> hobbySet = data()
            .stream()
            .map(p -> p.getHobby().split(","))
            .flatMap(Stream::of)
            .collect(Collectors.toSet());
    System.out.println(hobbySet);
    // 方式2:直接将单个元素映射为 Stream
    hobbySet = data()
            .stream()
            .flatMap(p -> Stream.of(p.getHobby().split(",")))
            .collect(Collectors.toSet());
    System.out.println(hobbySet);
}

输出:

[War3, CS:GO, LOL, DOTA, Swimming, RedAlert2, Journey, Food, Beer, Jogging]
[War3, CS:GO, LOL, DOTA, Swimming, RedAlert2, Journey, Food, Beer, Jogging]

Terminal opt-Collectors.mapping

mapping 函数:在聚合元素时,对元素进行映射转换。若处理元素的中间操作阶段进行了map,那么此时属于二次map

参数:

  • Function mapper:元素的映射过程。
  • Collector downstream:对于映射后的元素的采集过程。

栗子:

Stream.of("1", "2", "3").map(Integer::parseInt).collect(Collectors.toList());
Stream.of("1", "2", "3").collect(Collectors.mapping(Integer::parseInt, Collectors.toList()));

这两行代码效果是一样的,不过更推荐前者。

那么既然可以通过map实现同样效果,为什么不直接使用map的方式呢?因为在实际应用中,编写一些复杂处理:处理分组后的下游数据,Collectors.mapping更适用。

栗子:

/**
 * 根据年龄分组,并统计每组下的成员姓名
 */
@Test
public void test2() {
    Map<Integer, Set<String>> ageNameMapping = data()
            .stream()
            .collect(
                    Collectors.groupingBy(
                            Person::getAge,
                            Collectors.mapping(Person::getName, Collectors.toSet())
                    )
            );
    System.out.println(ageNameMapping);
}

输出:

{40=[孙七, 赵六], 25=[李四, 张三], 30=[王五]}

Terminal opt-Collectors.toCollection&collectingAndThen

开发时,经常会遇到根据指定字段进行分组的情况。有时需要对分组时产生的下游数据进行其他操作,个人最经常操作的就是排序。

toCollection函数: 对聚合元素使用的容器进行定制化。

参数:

Supplier collectionFactory:定义装载元素的容器。

collectingAndThen函数: 聚合元素完毕后,对返回容器进行二次处理。

参数:

  • Collector downstream:聚合元素的过程。
  • Function finisher:对downstream参数返回的容器的二次处理过程。处理后,需要将容器返回。

栗子:

/**
 * 根据年龄分组,每组根据生日进行排序
 */
@Test
public void test3() {
    // 通过定制特定数据结构的容器实现排序:正序
    Map<Integer, Collection<Person>> ageMapping = data()
            .stream()
            .collect(
                    Collectors.groupingBy(
                            Person::getAge,
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getBirthday)))
                    )
            );
    printMap(ageMapping);
    // 通过定制特定数据结构的容器实现排序:逆序
    ageMapping = data()
            .stream()
            .collect(
                    Collectors.groupingBy(
                            Person::getAge,
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person::getBirthday).reversed()))
                    )
            );
    printMap(ageMapping);
    // 通过对聚合元素后返回的容器二次处理,实现排序
    ageMapping = data()
            .stream()
            .collect(
                    Collectors.groupingBy(
                            Person::getAge,
                            Collectors.collectingAndThen(
                                    Collectors.toList(),
                                    l -> {
                                        l.sort(Comparator.comparing(Person::getBirthday).reversed());
                                        return l;
                                    }
                            )
                    )
            );
    printMap(ageMapping);
}
public void printMap(Map<Integer, Collection<Person>> mapping) {
    DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    mapping.forEach((key, val) -> System.out.println(key + " : " + val.stream().map(p -> p.getName() + " -> " + dateTimeFormatter.format(p.getBirthday())).collect(Collectors.joining(" / "))));
    System.out.println();
}

输出:

40 : 赵六 -> 1982-04-01 00:00:00 / 孙七 -> 1982-05-01 00:00:00
25 : 张三 -> 1997-01-01 00:00:00 / 李四 -> 1997-02-01 00:00:00
30 : 王五 -> 1992-03-01 00:00:00

40 : 孙七 -> 1982-05-01 00:00:00 / 赵六 -> 1982-04-01 00:00:00
25 : 李四 -> 1997-02-01 00:00:00 / 张三 -> 1997-01-01 00:00:00
30 : 王五 -> 1992-03-01 00:00:00

40 : 孙七 -> 1982-05-01 00:00:00 / 赵六 -> 1982-04-01 00:00:00
25 : 李四 -> 1997-02-01 00:00:00 / 张三 -> 1997-01-01 00:00:00
30 : 王五 -> 1992-03-01 00:00:00

Terminal opt-Collectors.toMap

有时我们分组后,可以确定每组的值是一个单值,而不是多值。这种情况下就可以使用toMap,避免取值时的不便。

toMap函数:将聚合的元素组装为一个Map返回。

参数:

  • Function keyMapper:分组时使用的 Key
  • Function valueMapper:将 Key 匹配元素的什么值作为 Key 的 Value
  • BinaryOperator mergeFunction:若发生单 Key 匹配到多个元素时的合并过程(只能返回一个元素作为 Key 的 Value)
  • Supplier mapSupplier:指定装载元素的 Map 类型容器

栗子:

/**
 * 根据年龄分组,只保留生日最大的成员
 */
@Test
public void test4() {
    // 下面注释代码会抛出异常,因为没有指定当单Key匹配到多值时的 merge 行为
    // 源码中的默认指定的 meger 行为则是抛出异常:throwingMerger()
    // Map<Integer, Person> toMap = data().stream().collect(Collectors.toMap(Person::getAge, Function.identity()));
    Map<Integer, Person> toMap = data()
            .stream()
            .collect(
                    Collectors.toMap(
                            Person::getAge,
                            Function.identity(),
                            (v1, v2) -> Comparator.comparing(Person::getBirthday).compare(v1, v2) > 0 ? v1 : v2
                    )
            );
    toMap.forEach((key, val) -> System.out.println(key + " : " + val.getName() + " -> " + DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(val.getBirthday())));
}

输出:

40 : 孙七 -> 1982-05-01 00:00:00
25 : 李四 -> 1997-02-01 00:00:00
30 : 王五 -> 1992-03-01 00:00:00

到此这篇关于Collection stream使用示例详解的文章就介绍到这了,更多相关Collection stream内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java中的Collections类的使用示例详解

    Collections的常用方法及其简单使用 代码如下: package Collections; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Stack; public class collections { public static void main(String[]args){ int array[]={125,75,56,7}; Li

  • Java Collection集合的三种遍历方式详解

    目录 Collection遍历方式 迭代器遍历 foreach遍历 Lambda遍历 Collection遍历方式 Collection集合遍历的方式有三种: 迭代器foreach/增强for循环lambda表达式 迭代器遍历 迭代器概述: 遍历就是一个一个的把容器中的元素访问一遍. 迭代器在Java中是Iterator代表的,迭代器是集合的专用遍历方式. Collection集合获取迭代器的方: 方法名称 说明 iterator() 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引

  • Java集合的定义与Collection类使用详解

    什么是集合? 概念:对象的容器,定义了对多个对象进行操作的常用方法.可实现数组的功能. 集合和数组的区别: 数组长度固定,集合长度不固定 数组可以存储基本类型和引用类型,集合只能引用类型 Collection : Collection体系结构: Collection的使用:包括增加元素.删除元素.遍历元素(两种方法)和判断 直接看代码: package com.collections; import java.util.ArrayList; import java.util.Collection

  • Collection stream使用示例详解

    目录 基础数据 元素转Stream Terminal opt-Collectors.mapping Terminal opt-Collectors.toCollection&collectingAndThen Terminal opt-Collectors.toMap 近日频繁应用 Stream 的 Api,记录一下应用实例. 基础数据 实体类: @Data @Accessors(chain = true) public static class Person { private String

  • Java 数据结构算法Collection接口迭代器示例详解

    目录 Java合集框架 Collection接口 迭代器 Java合集框架 数据结构是以某种形式将数据组织在一起的合集(collection).数据结构不仅存储数据,还支持访问和处理数据的操作 在面向对象的思想里,一种数据结构也被认为是一个容器(container)或者容器对象(container object),它是一个能存储其他对象的对象,这里的其他对象常被称为数据或者元素 定义一种数据结构从实质上讲就是定义一个类.数据结构类应该使用数据域存储数据,并提供方法支持查找.插入和删除等操作 Ja

  • Java Stream流语法示例详解

    目录 如何使用Stream? Stream的操作分类 1.创建流 2.操作流 1)过滤 2)映射 3)匹配 4)组合 3.转换流 如何使用Stream? 聚合操作是Java 8针对集合类,使编程更为便利的方式,可以与Lambda表达式一起使用,达到更加简洁的目的. 前面例子中,对聚合操作的使用可以归结为3个部分: 1)  创建Stream:通过stream()方法,取得集合对象的数据集. 2)  Intermediate:通过一系列中间(Intermediate)方法,对数据集进行过滤.检索等数

  • Java8如何构建一个Stream示例详解

    Stream初体验 Stream是Java8中操作集合的一个重要特性,我们先来看看Java里面是怎么定义Stream的: "A sequence of elements supporting sequential and parallel aggregate operations." 我们来解读一下上面的那句话: 1.Stream是元素的集合,这点让Stream看起来用些类似Iterator: 2.可以支持顺序和并行的对原Stream进行汇聚的操作. Stream的创建方式有很多种,除

  • java EasyExcel面向Excel文档读写逻辑示例详解

    目录 正文 1 快速上手 1.1 引入依赖 1.2 导入与导出 2 实现原理 2.1 @RequestExcel 与 @ResponseExcel 解析器 2.2 RequestMappingHandlerAdapter 后置处理器 3 总结 正文 EasyExcel是一款由阿里开源的 Excel 处理工具.相较于原生的Apache POI,它可以更优雅.快速地完成 Excel 的读写功能,同时更加地节约内存. 即使 EasyExcel 已经很优雅了,但面向 Excel 文档的读写逻辑几乎千篇一

  • Java8新特性Stream流实例详解

    什么是Stream流? Stream流是数据渠道,用于操作数据源(集合.数组等)所生成的元素序列. Stream的优点:声明性,可复合,可并行.这三个特性使得stream操作更简洁,更灵活,更高效. Stream的操作有两个特点:可以多个操作链接起来运行,内部迭代. Stream可分为并行流与串行流,Stream API 可以声明性地通过 parallel() 与sequential() 在并行流与顺序流之间进行切换.串行流就不必再细说了,并行流主要是为了为了适应目前多核机器的时代,提高系统CP

  • .NetCore实现上传多文件的示例详解

    本章和大家分享的是.NetCore的MVC框架上传文件的示例,主要讲的内容有:form方式提交上传,ajax上传,ajax提交+上传进度效果,Task并行处理+ajax提交+上传进度,相信当你读完文章内容后能后好的收获,如果可以不妨点个赞:由于昨天电脑没电了,快要写完的内容没有保存,今天早上提前来公司从头开始重新,断电这情况的确让人很头痛啊,不过为了社区的分享环境,这也是值得的,不多说了来进入今天的正篇环节吧: form方式上传一组图片 先来看看咋们html的代码,这里先简单说下要上传文件必须要

  • C++中#include头文件的示例详解

    fstream是C++ STL中对文件操作的合集,包含了常用的所有文件操作.在C++中,所有的文件操作,都是以流(stream)的方式进行的,fstream也就是文件流file stream. 最常用的两种操作为: 1.插入器(<<) 向流输出数据.比如说打开了一个文件流fout,那么调用fout<<"Write to file"<<endl;就表示把字符串"Write to file"写入文件并换行. 2.析取器(>>

  • Springboot自动扫描包路径来龙去脉示例详解

    我们暂且标注下Springboot启动过程中较为重要的逻辑方法,源码对应的spring-boot-2.2.2.RELEASE版本 public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBoo

  • golang编程开发使用sort排序示例详解

    golang sort package: https://studygolang.com/articles/3360 sort 操作的对象通常是一个 slice,需要满足三个基本的接口,并且能够使用整数来索引 // A type, typically a collection, that satisfies sort.Interface can be // sorted by the routines in this package. The methods require that the /

随机推荐