跟我学 Java 8 新特性之 Stream 流(六)收集

转载自   跟我学 Java 8 新特性之 Stream 流(六)收集

我们前面的五篇文章基本都是在说将一个集合转成一个流,然后对流进行操作,其实这种操作是最多的,但有时候我们也是需要从流中收集起一些元素,并以集合的方式返回,我们把这种反向操作称为收集。

流API也给我们提供了相应的方法。

如何在流中使用收集功能?

我们先看一看流API给我们提供的方法:

public interface Stream<T> extends BaseStream<T, Stream<T>> {//...忽略那些不重要的东西<R> R collect(Supplier<R> supplier,BiConsumer<R, ? super T> accumulator,BiConsumer<R, R> combiner);<R, A> R collect(Collector<? super T, A, R> collector);}

流API中给我们提供了两种,我给大家分析一下。

R collect(Collector collector);

其中R指定结果的类型,T指定了调用流的元素类型。内部积累的类型由A指定。collectorFunc是一个收集器,指定收集过程如何执行,collect()方法是一个终端方法。

虽然我们基本上很少会用到自定义的collectorFunc,但是了为扩展大家的知识面,我们还是简单地聊一聊Collector,Because it's my style!

Collector接口位于 java.util.stream包中的声明,它的容颜是这样的:

package java.util.stream;public interface Collector<T, A, R> {Supplier<A> supplier();BiConsumer<A, T> accumulator();BinaryOperator<A> combiner();Function<A, R> finisher();}

其中T、A、R的含义和上面是一样的 其中R指定结果的类型,T指定了调用流的元素类型。内部积累的类型由A指定

但是这一篇我们不实现他们,因为JDK已经给我们提供了很强大的方法了,他们位于 java.util.stream下面的 Collectors类,我们本篇也主要是使用 Collectors来实现收集的功能。

Collectors类是一个最终类,里面提供了大量的静态的收集器方法,借助他,我们基本可以实现各种复杂的功能了。

我们来看一下toList和toSet方法:

public static <T>  Collector<T, ?, List<T>> toList()public static <T> Collector<T, ?, Set<T>> toSet()

其中 Collectors#toList()返回的收集器可以把流中元素收集到一个List中, Collectors#toSet()返回的收集器可以把流中的元素收集到一个Set中。比如:如果你想把元素收集到List中,你可以这样用, steam.collect(Collectors.toList)

接下来,我们把我们的王者荣耀团队经济例子修改一下,把明星玩家和当前获得的金币数收集到一个List里面,把出场的英雄收集到一个Set里面:

#玩家使用的英雄以及当前获得的金币数
public class HeroPlayerGold {/** 使用的英雄名字 */private String hero;/** 玩家的ID */private String player;/** 获得的金币数 */private int gold;public HeroPlayerGold(String hero, String player, int gold) {this.hero = hero;this.player = player;this.gold = gold;}@Overridepublic String toString() {return "HeroPlayerGold{" +"hero='" + hero + '\'' +", player='" + player + '\'' +", gold=" + gold +'}';}
//省略get/set}#出场的英雄
public class Hero {/** 使用的英雄名字 */private String hero;public Hero(String hero) {this.hero = hero;}@Overridepublic String toString() {return "Hero{" +"hero='" + hero + '\'' +'}';}
//省略get/set
}#测试类
public class Main {public static void main(String[] args) {learnCollect();}private static void learnCollect() {List<HeroPlayerGold> lists = new ArrayList<>();lists.add(new HeroPlayerGold("盖伦", "RNG-Letme", 100));lists.add(new HeroPlayerGold("诸葛亮", "RNG-Xiaohu", 300));lists.add(new HeroPlayerGold("露娜", "RNG-MLXG", 300));lists.add(new HeroPlayerGold("狄仁杰", "RNG-UZI", 500));lists.add(new HeroPlayerGold("牛头", "RNG-Ming", 500));List<PlayerGold> playerGolds = lists.stream().map(plary -> new PlayerGold(plary.getPlayer(), plary.getGold())).collect(Collectors.toList());System.out.println("============PlayerGold begin==============");playerGolds.forEach(System.out::println);System.out.println("============PlayerGold end================\n");Set<Hero> heroes = lists.stream().map(player -> new Hero(player.getHero())).collect(Collectors.toSet());System.out.println("============Hero begin==============");heroes.forEach(System.out::println);System.out.println("============Hero end================");}}

输出的日志:

  1. ============PlayerGold begin==============

  2. PlayerGold{player='RNG-Letme', gold=100}

  3. PlayerGold{player='RNG-Xiaohu', gold=300}

  4. PlayerGold{player='RNG-MLXG', gold=300}

  5. PlayerGold{player='RNG-UZI', gold=500}

  6. PlayerGold{player='RNG-Ming', gold=500}

  7. ============PlayerGold end================

  8.  

  9. ============Hero begin==============

  10. Hero{hero='露娜'}

  11. Hero{hero='牛头'}

  12. Hero{hero='盖伦'}

  13. Hero{hero='狄仁杰'}

  14. Hero{hero='诸葛亮'}

  15. ============Hero end================

看到这里,大家有感受到流API的威力了吗?提示一下,封装一个工具类,然后结合一FastJson这种东西一起使用!是真的好用啊!其实将数据从集合移到流中,或者将数据从流移回集合的能力,是流API给我们提供的一个强大特性,因为这允许通过流来操作集合,然后把流重新打包成集合。此外,条件合适的时候,让流操作并行发生,提高效率。

接下来我们分析第二个方法,

<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);

我们第二个版本的收集方法,主要是可以在收集的过程中,给予更多的控制。其中supplier指定如何创建用于保存结果的对象,比如,要使用ArrayList作为结果的集合,需要指定它的构造函数,accumulator函数是将一个元素添加到结果中,而combiner函数合并两个部分的结果。

大家应该发现了吧,他的工作方式和我们第三篇介绍缩减操作时的reduce方法是很像的。它们都必须是无状态和不干预的,并且必须有关联性,三个约束条件缺一不可。

Supplier也是 java.util.function包中的一个函数式接口:

@FunctionalInterface
public interface Supplier<T> {T get();
}

只有一个get(),并且是没有参数的,在collect()方法返回一个R类型的对象,并且get()方法返回一个指向集合的引用。

 

而accumulator,combiner的类型是 BiConsumer,他们也是 java.util.function包中的一个函数式接口:

@FunctionalInterface
public interface BiConsumer<T, U> {void accept(T t, U u);
}

其中t,u执行某种类型的操作,对于accumulator来说,t指定了目标集合,u指定了要添加到该集合的元素。对于combiner来说,t和u指定的是两个要被合并的集合。

我们把前面的例子改变一下,然后也详细地说一下,在没有用lambda和使用lambda之后的区别:

这个是没有使用lambda前的:

private static void learnCollect() {List<HeroPlayerGold> lists = new ArrayList<>();lists.add(new HeroPlayerGold("盖伦", "RNG-Letme", 100));lists.add(new HeroPlayerGold("诸葛亮", "RNG-Xiaohu", 300));lists.add(new HeroPlayerGold("露娜", "RNG-MLXG", 300));lists.add(new HeroPlayerGold("狄仁杰", "RNG-UZI", 500));lists.add(new HeroPlayerGold("牛头", "RNG-Ming", 500));lists.stream().collect(new Supplier<HashSet<HeroPlayerGold>>() {@Overridepublic HashSet<HeroPlayerGold> get() {return new HashSet<>();}},//第一个参数new BiConsumer<HashSet<HeroPlayerGold>, HeroPlayerGold>() {@Overridepublic void accept(HashSet<HeroPlayerGold> heroPlayerGolds, HeroPlayerGold heroPlayerGold) {heroPlayerGolds.add(heroPlayerGold);}},//第二个参数new BiConsumer<HashSet<HeroPlayerGold>, HashSet<HeroPlayerGold>>() {@Overridepublic void accept(HashSet<HeroPlayerGold> heroPlayerGolds, HashSet<HeroPlayerGold> heroPlayerGolds2) {heroPlayerGolds.addAll(heroPlayerGolds2);}}//第三个参数).forEach(System.out::println);}

在没有使用lambda前,虽然看起来的让人眼花缭乱的,但不得不说,他其实能帮助我们实现非常强大的功能,我们自定义的收集过程,全部都可以交给这个家伙,我们用lambda整理一下:

private static void learnCollect() {List<HeroPlayerGold> lists = new ArrayList<>();lists.add(new HeroPlayerGold("盖伦", "RNG-Letme", 100));lists.add(new HeroPlayerGold("诸葛亮", "RNG-Xiaohu", 300));lists.add(new HeroPlayerGold("露娜", "RNG-MLXG", 300));lists.add(new HeroPlayerGold("狄仁杰", "RNG-UZI", 500));lists.add(new HeroPlayerGold("牛头", "RNG-Ming", 500));lists.stream().collect(() -> new HashSet<>(),(set,elem)->set.add(elem),(setA,setB)->setA.addAll(setB)).forEach(System.out::println);}大家以为到这里就结束了吗?其实还可以使用方法引用和构造函数引用来简化:
private static void learnCollect() {List<HeroPlayerGold> lists = new ArrayList<>();lists.add(new HeroPlayerGold("盖伦", "RNG-Letme", 100));lists.add(new HeroPlayerGold("诸葛亮", "RNG-Xiaohu", 300));lists.add(new HeroPlayerGold("露娜", "RNG-MLXG", 300));lists.add(new HeroPlayerGold("狄仁杰", "RNG-UZI", 500));lists.add(new HeroPlayerGold("牛头", "RNG-Ming", 500));lists.stream().collect(HashSet::new,HashSet::add,HashSet::addAll).forEach(System.out::println);
}

小结一下

本篇带大家入门了Stream的收集操作,但是有了些这入门操作,我相信,你在我的演变过程中已经发现了扩展点了,不管是supplier,accumulator还是combiner,都可以在里面放一些特别的操作进去,从而满足你们的各种要求。

另外一个点,大家一定不要忘记了Collectors这个最终类,里面已经提供了很多很强大的静态方法,如果你们遇到一些特别的需求,首先要想到的应该是Collectors,如果里面的方法都不能实现你的要求,再考虑通过第二个版本的collect()方法实现你的自定义收集过程吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/325486.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android解决工具类中没有context中的问题

有时我们需要在一个工具类中用到上下文context。当然有一个做法就是使用传递context的方法&#xff0c;当我们需要用全局context的时候&#xff0c;该怎么办呢&#xff1f; 其实我们应用启动的时候会启动Application这个类&#xff0c;这个类是在AndroidManifest.xml文件里其实…

中控指纹采集器开发指纹识别项目(说明)

历史指纹识别相关开发版本&#xff1a;指纹识别开发1.0&#xff0c;开发时间&#xff1a;2018-01-04 指纹识别开发2.0&#xff0c;开发时间&#xff1a;2018-01-04指纹识别开发3.0&#xff0c;开发时间&#xff1a;2020-01-06可以从时间上看的出来&#xff0c;在2018年1月4日&a…

JS生成x到y的随机数

/** Math.random()* - 可以用来生成一个0-1之间的随机数* - 生成一个0-10的随机数* - 生成一个0-x之间的随机数* Math.round(Math.random()*x)* * - 生成一个1-10* - 生成一个x-y之间的随机数* Math.round(Math.random()*(y-x)x)*//*for(var i0 ; i<100 ; i){//con…

MSSQL-Scripter,一个新的生成T-SQL脚本的SQL Server命令行工具

这里向大家介绍一个新的生成T-SQL脚本的SQL Server命令行工具&#xff1a;mssql-scripter。它支持在SQL Server、Azure SQL DB以及Azure SQL DW中为数据库生成CREATE和INSERT T-SQL脚本。 Mssql-scripter是一个跨平台的命令行工具&#xff0c;功能等同于SQL Server Management…

用startSmoothScroll实现RecyclerView滚动到指定位置并置顶,含有动画。

RecyclerView滚动到指定位置并置顶 RecyclerView本身提供了几个定位的方法&#xff0c;除了手动滑动的scrollTo&#xff0c;smootScrollTo和scrollBy&#xff0c;smoothScrollBy方法之外&#xff0c;有一个直接滑动到指定位置item的scrollToPosition方法和另一个在此基础上平滑…

跟我学 Java 8 新特性之 Stream 流(七)流与迭代器,流系列大结局

转载自 跟我学 Java 8 新特性之 Stream 流&#xff08;七&#xff09;流与迭代器&#xff0c;流系列大结局 恭喜你们&#xff0c;马上就要学完Java8 Stream流的一整系列了&#xff0c;其实我相信Stream流对很多使用Java的同学来说&#xff0c;都是一个知识盲点&#xff0c;因…

重要说明,粉丝必看【java人脸识别说明】

重要通知关于人脸识别简要说明&#xff1a; 源码即日起由免费改为收费。以下是微信收款码&#xff0c;如果有需要可以直接扫码转账即可。&#xff08;注意&#xff0c;源码均为测试好的&#xff0c;故各位在开发的过程中遇到的源码问题不提供任何技术支持。【转账完成之后可以直…

包装类

如果用包装类可以打印出来 <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title></title><script type"text/javascript">var s123;snew String(s);s.hello"nihao";console.log(s.hello);<…

深入理解C#:编程技巧总结(二)

以下总结参阅了&#xff1a;MSDN文档、《C#高级编程》、《C#本质论》、前辈们的博客等资料&#xff0c;如有不正确的地方&#xff0c;请帮忙及时指出&#xff01;以免误导&#xff01; 在上一篇 深入理解C#&#xff1a;编程技巧总结&#xff08;一&#xff09; 中总结了25点&a…

pagerslidingtabstrip 横向滑动

pagerslidingtabstrip 横向滑动 配合ViewPager

Java 虚拟机对锁优化所做的努力

转载自 Java 虚拟机对锁优化所做的努力 作为一款公用平台&#xff0c;JDK 本身也为并发程序的性能绞尽脑汁&#xff0c;在 JDK 内部也想尽一切办法提供并发时的系统吞吐量。这里&#xff0c;我将向大家简单介绍几种 JDK 内部的 "锁" 优化策略。 1、 锁偏向 锁偏…

不一样的假期,到底哪里不一样?

目录&#xff1a;1.回访17级同学们&#xff0c;了解同学们目前的工作情况2.和18级同学们聊天&#xff0c;了解假期在家的学习情况3.检查19级同学们提交至小程序内的作业。回忆17级前两天将我带的17级毕业班挨个找了一遍&#xff0c;大致了解了下大家最近的工作情况&#xff0c;…

JS字符串和正则表达式

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><script type"text/javascript">var str "1a2b3c4d5e6f7";/** split()* - 可以将一个字符串拆分为一个数组* - 方法中可以传递…

TensorFlowSharp入门使用C#编写TensorFlow人工智能应用

TensorFlowSharp入门使用C#编写TensorFlow人工智能应用学习。 TensorFlow简单介绍 TensorFlow 是谷歌的第二代机器学习系统&#xff0c;按照谷歌所说&#xff0c;在某些基准测试中&#xff0c;TensorFlow的表现比第一代的DistBelief快了2倍。 TensorFlow 内建深度学习的扩展支…

Mybatis 模糊查询

第一种&#xff1a;&#xff08;推荐&#xff09; select * from user where username like #{abc}执行保存操作List<User> users dao.findUserByName("保"); 日志&#xff1a;> Preparing: select * from user where username like %保%; > Parameter…

Spring Cloud 升级最新 Finchley 版本,踩了所有的坑

转载自 Spring Cloud 升级最新 Finchley 版本&#xff0c;踩了所有的坑 Spring Boot 2.x 已经发布了很久&#xff0c;现在 Spring Cloud 也发布了 基于 Spring Boot 2.x 的 Finchley 版本&#xff0c;现在一起为项目做一次整体框架升级。 升级前 > 升级后 Spring Boot …

快来看看你们的新年礼物,猜猜是什么?

春节总把新桃换旧符千门万户曈曈日春风送暖入屠苏爆竹声中一岁除新年礼物前言各位同学们&#xff0c;新春快乐哇&#xff0c;利用假期的时间&#xff0c;花费5天左右的时间&#xff0c;为大家每个人准备了一份神秘的新年礼物&#xff0c;想不想知道是什么吗&#xff1f;必看那么…

行动力决定了一个人的成败,有想法,就去做! C#的内存管理原理解析+标准Dispose模式的实现

尽管.NET运行库负责处理大部分内存管理工作&#xff0c;但C#程序员仍然必须理解内存管理的工作原理&#xff0c;了解如何高效地处理非托管的资源&#xff0c;才能在非常注重性能的系统中高效地处理内存。C#编程的一个优点就是程序员不必担心具体的内存管理&#xff0c;垃圾回收…

求从 1 到 100 之间所有奇数的平方和

public static void test15(){int i 1; //循环的初始值int sum 0;//放和的变量 while(i<100){if(i%2!0){sum sum(i*i); }i;}System.out.println("平方和是&#xff1a;"sum);}