Collection stream使用示例详解

 更新时间:2022年12月19日 17:00:11   作者:码畜c  
这篇文章主要介绍了Collection stream使用示例,stream流几乎可以完成对集合的任意操作,映射、去重、分组、排序、过滤等

近日频繁应用 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内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决java编译错误:程序包不存在的问题

    解决java编译错误:程序包不存在的问题

    出错:Error:(3, 27) java: 程序包com.aliyun.odps.udf不存在,遇到这样的错误问题如何解决呢,下面小编给大家带来了java编译错误:程序包不存在的问题及解决方法,感兴趣的朋友一起看看吧
    2023-05-05
  • 关于springboot 配置date字段返回时间戳的问题

    关于springboot 配置date字段返回时间戳的问题

    这篇文章主要介绍了springboot 配置date字段返回时间戳的问题,在springboot2.0后,spring会将Date字段自动给转成UTC字符串了(在没有配置的情况下),所以date需要转换成时间戳还是yyyy-MM-dd HH:mm:ss,具体解决方法跟随小编一起看看吧
    2021-07-07
  • springboot+vue制作后台管理系统项目

    springboot+vue制作后台管理系统项目

    本文详细介绍了后台管理使用springboot+vue制作,以分步骤、图文的形式详细讲解,大家有需要的可以参考参考
    2021-08-08
  • 自定义log4j.properties的加载位置方式

    自定义log4j.properties的加载位置方式

    这篇文章主要介绍了自定义log4j.properties的加载位置方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • Java实现从数据库导出大量数据记录并保存到文件的方法

    Java实现从数据库导出大量数据记录并保存到文件的方法

    这篇文章主要介绍了Java实现从数据库导出大量数据记录并保存到文件的方法,涉及Java针对数据库的读取及文件写入等操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-11-11
  • Java关键字之native详解

    Java关键字之native详解

    这篇文章主要为大家介绍了Java关键字之native,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-01-01
  • SpringBoot集成JWT生成token及校验方法过程解析

    SpringBoot集成JWT生成token及校验方法过程解析

    这篇文章主要介绍了SpringBoot集成JWT生成token及校验方法过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • SpringBoot整合Mybatis-Plus实现微信注册登录的示例代码

    SpringBoot整合Mybatis-Plus实现微信注册登录的示例代码

    微信是不可或缺的通讯工具,本文主要介绍了SpringBoot整合Mybatis-Plus实现微信注册登录的示例代码,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的可以了解一下
    2024-02-02
  • Java的枚举,注解和反射(二)

    Java的枚举,注解和反射(二)

    今天小编就为大家分享一篇关于Java枚举,注解与反射原理说明,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2021-07-07
  • Java数据结构之堆(优先队列)的实现

    Java数据结构之堆(优先队列)的实现

    堆(优先队列)是一种典型的数据结构,其形状是一棵完全二叉树,一般用于求解topk问题。本文将利用Java语言实现堆,感兴趣的可以学习一下
    2022-05-05

最新评论