前面的一系列博客中,我们都是从一个集合中拿到一个流,但是有时候需要执行反操作,就是从流中获得集合。实际编码中,当我们处理完流后,我们通常想查看下结果,而不是将他们聚合成一个值。我们可以调用iterator方法来生成一个传统风格的迭代器,用于访问元素。

  • 1,将流中的元素收集到一个数组中,调用toArray()方法即可

由于无法在运行时创建一个泛型数组,所以调用toArray()方法将返回一个object[]数组。如果我们希望得到一个正确的数组,可以将类型传递给数组的构造函数toArray(String[]::new)。

public static void main(String[] args)
{
Stream<String> stream = Stream.of("林肯", "冰儿");
String[] array = stream.toArray(String[]::new);
for (String str : array)
{
System.out.println(str);
}
}
  • 2,将流中的元素收集到一个集合中。

流API提供了一个collect()方法,我们可以对比stream()方法,来很简单的实现集合和流的互相转换。

翻一下API说明文档,上面是这么定义这2个方法的:

<R,A> R 	collect(Collector<? super T,A,R> collector):Performs a mutable reduction operation on the elements of this stream using a Collector.
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner):Performs a mutable reduction operation on the elements of this stream.

关于Collector接口我们了解下好了,这个接口的申明如下:

interface Collector<T,A,R>,R指定结果的类型,T指定调用流的元素类型,内部累积类型由A指定。

Collectors类为了我们定义了一些可以直接使用的静态收集器方法,这是一个不可变类,我们一般使用toList()和toSet()方法,如下所示:

public static <T> Collector<T, ?, List<T>> toList()
{
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add, (left, right) ->
{
left.addAll(right);
return left;
} , CH_ID);
} public static <T> Collector<T, ?, Set<T>> toSet()
{
return new CollectorImpl<>((Supplier<Set<T>>) HashSet::new, Set::add, (left, right) ->
{
left.addAll(right);
return left;
} , CH_UNORDERED_ID);
}

toList()方法返回的收集器可用于将元素收集到一个list中,toSet()方法返回的收集器可用于将元素收集到一个Set中。例如,我们现在想要把元素收集到一个list中,可以向下面这样子调用:

public static void main(String[] args) throws Exception
{
List<Double> list = new ArrayList<>(4);
list.add(1.1);
list.add(2.5);
list.add(3.0);
list.add(4.8);
Stream<Double> parallelStream = list.parallelStream();
List<Double> collect = parallelStream.collect(Collectors.toList());
collect.forEach(System.out::println);
}

上面的整理我们使用Collectors的静态方法比较方便,当然我们也看到了上面的collect方法还有另外一个重载,我们使用这个版本可以对收集过程做更多的控制。

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

关于这个方法签名多说几句,上面方法中的3个参数:

supplier:表示一个指向可变存储对象的引用;后面2个参数都是BiConsumer<R,R> 类型的,这个函数式接口表示对2个对象进行处理,它里面的抽象方法签名如下:void accept(T obj1,U obj2),对于上面第2个参数,obj1指定目标集合,obj2指定要添加到该集合中的元素,对于上面的第3个参数,obj1和obj2指定两个将要被合并的集合。

具体的使用看下面代码:

public static void main(String[] args) throws Exception
{
List<Double> list = new ArrayList<>(4);
list.add(1.1);
list.add(2.5);
list.add(3.0);
list.add(4.8);
Stream<Double> parallelStream = list.parallelStream();
//使用Lambda表达式
parallelStream.collect(() -> new LinkedList<>(), (MyList, element) -> MyList.add(element), (listA, listB) -> listA.addAll(listB))
.forEach(System.out::println);
//使用方法引用,直接使用被消费过的流报错stream has already been operated upon or closed,所以要重新获得流
List<Double> list1 = list.parallelStream().collect(LinkedList::new,LinkedList::add,LinkedList::addAll);
list1.forEach(System.out::println);
}
  • 3,将流中的元素收集到一个map中

和上面的处理list一样,使用Collectors的toMap()方法就可以将一个流中的元素收集到一个map中。该方法有2个参数,分别用来生成map的键和值。

public class Test
{ public static void main(String[] args)
{
Stream<Person> stream = Stream.of(new Person("1", "林肯"), new Person("2", "冰儿"));
Map<String, String> collect = stream.collect(Collectors.toMap(Person::getId, Person::getName));
for (String string : collect.keySet())
{
System.out.println(string + "-->" + collect.get(string));
} for (Map.Entry<String, String> entry : collect.entrySet())
{
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
System.out.println("==========华丽丽的分割线============"); Stream<Person> stream1 = Stream.of(new Person("1", "林肯"), new Person("2", "冰儿"));
//如果要获得原来的元素,使用Function.identity()就OK啦
Map<String, Person> map = stream1.collect(Collectors.toMap(Person::getId, Function.identity()));
for (String string : map.keySet())
{
System.out.println(string + "-->" + map.get(string).toString());
}
} } class Person
{
private String id;
private String name; public Person(String id, String name)
{
this.id = id;
this.name = name;
} public String getId()
{
return id;
} public void setId(String id)
{
this.id = id;
} public String getName()
{
return name;
} public void setName(String name)
{
this.name = name;
} @Override
public String toString()
{
return "Person [id=" + id + ", name=" + name + "]";
} }

关于这点要注意的是,如果有相同的元素拥有相同的键,那么收集方法就会报错:java.lang.IllegalStateException

public static void main(String[] args)
{
Stream<Person> stream = Stream.of(new Person("1", "林肯"), new Person("1", "冰儿"));
//下面代码报错,java.lang.IllegalStateException:
Map<String, String> collect = stream.collect(Collectors.toMap(Person::getId, Person::getName));
for (String string : collect.keySet())
{
System.out.println(string + "-->" + collect.get(string));
} }

那么怎么办呢?

我们可以提供第3个函数参数,根据已有的值和新值来决定键的值,从而重新该行为。

public static void main(String[] args)
{
Stream<Person> stream = Stream.of(new Person("1", "林肯"), new Person("1", "冰儿"));
//下面代码输出:1-->林肯冰儿
Map<String, String> collect = stream.collect(Collectors.toMap(Person::getId, Person::getName,(value1,value2)->value1+value2));
for (String string : collect.keySet())
{
System.out.println(string + "-->" + collect.get(string));
} }

流API--流的收集的更多相关文章

  1. JAVA8给我带了什么——流的概念和收集器

    到现在为止,笔者不敢给流下定义,从概念来讲他应该也是一种数据元素才是.可是在我们前面的代码例子中我们可以看到他更多的好像在表示他是一组处理数据的行为组合.这让笔者很难去理解他的定义.所以笔者不表态.各 ...

  2. 响应式流API的构建基础

    下面三个重要的概念是响应式流API的构建基础: 发布者是事件的发送方,可以向它订阅. 订阅者是事件订阅方. 订阅将发布者和订阅者联系起来,使订阅者可以向发布者发送信号. http://www.info ...

  3. java8-Stream流API

    一回顾与说明 经过前面发布的三章java8的博客,你就懂得了我们为什么要用Lamda表达式,Lamda表达式的原理与函数式接口的关系,从Lamda表达式到方法引用和构造引用. 想要学Stream流你必 ...

  4. Python 完美诠释"高内聚"概念的 IO 流 API 体系结构

    1. 前言 第一次接触 Python 语言的 IO API 时,是惊艳的.相比较其它语言所提供的 IO 流 API . 无论是站在使用者的角度还是站在底层设计者的角度,都可以称得上无与伦比. 很多人在 ...

  5. 详解ROMA Connect API 流控实现技术

    摘要:本文将详细描述API Gateway流控实现,揭开高性能秒级流控的技术细节. 1.概述 ROMA平台的核心系统ROMA Connect源自华为流程IT的集成平台,在华为内部有超过15年的企业业务 ...

  6. Java 基础 IO流(转换流,缓冲)

    一,前言 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamReader或者OutputStr ...

  7. java 文件字节和字符流 缓冲流

    流的原理 1) 在 Java 程序中,对于数据的输入/输出操作以“流”(stream) 方式进行:2) J2SDK 提供了各种各样的“流”类,用以获取不同种类的数据:程序中通过标准的方法输入或输出数据 ...

  8. [源码]ObjectIOStream 对象流 ByteArrayIOStream 数组流 内存流 ZipOutputStream 压缩流

    1.对象流 import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File ...

  9. IO流03_流的分类和概述

    [概述] Java的IO流是实现输入/输出的基础,它可以方便的实现数据的输入/输出操作. Java中把不同的输入/输出源(键盘.文件.网络连接)抽象表述为"流"(Stream). ...

  10. Java基础知识强化之IO流笔记41:字符流缓冲流之复制文本文件案例02(使用 [ newLine() / readLine() ] )(重要)

    1. 使用字符流缓冲流的特殊功能 [ newLine() / readLine() ] 需求:把当前项目目录下的a.txt内容复制到当前项目目录下的b.txt中  数据源: a.txt -- 读取数据 ...

随机推荐

  1. 6.while loop

    while 循环   有时候我们不确定需要循环几次.就像一个司机不知道自己需要什么时候加油一样.程序可以这样写:   while petrol_filling: increase price show ...

  2. 【算法】赫夫曼树(Huffman)的构建和应用(编码、译码)

    参考资料 <算法(java)>                           — — Robert Sedgewick, Kevin Wayne <数据结构>       ...

  3. django中数据库操作——in操作符

    django一对多关系中,为了实现按照一端查询多端,之前都是用extra操作符,经过查看官方文档还有in这个操作符,用这个实现更简单.直接上代码: 根据查询条件模糊查询所有符合条件的机架列表,然后根据 ...

  4. Python3——让我们像孩子一样的去看书

    用tkinter来画高级图形 让我们一起创造一个按钮吧!"按住我:0  :" 那我们先做一个小例子,让我们第一眼先认识到这个它(tkinter)吧! 输入以下代码: from tk ...

  5. Centos7 Zookeeper

    本文版权归博客园和作者吴双本人共同所有 转载和爬虫请注明原文地址 www.cnblogs.com/tdws 一.写在前面 ZK是一个高效的分布式协调服务,高可用的分布式管理协调框架. 朋友推荐一本书& ...

  6. 2017 Multi-University Training Contest - Team 1 1002&&HDU 6034 Balala Power!【字符串,贪心+排序】

    Balala Power! Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)T ...

  7. HDFS命令全总结

    .copyFromLocal hadoop fs -copyFromLocal -f dcnew.reg_user_actret.csv /dw/test/reg_user_actret_test # ...

  8. 如何给虚拟主机安装phpMyAdmin

    很多虚拟主机没有phpMyAdmin,例如阿里云的云虚拟主机默认的数据库管理工具是DMS,这样好多朋友管理数据库时会觉得不方便.phpMyAdmin是比较大众和常用的Mysql数据库管理软件,我们可以 ...

  9. SpringMVC框架学习笔记——各种异常、报错解决

    1.Target runtime com.genuitec.runtime.generic.jee60 is not defined. 找到导入项目的.setting文件夹org.eclipse.ws ...

  10. MongoDB模拟多文档事务操作

    Mongodb不支持多文档原子性操作,因此依据两阶段提交协议(Two Phase Commits protocol)来模拟事务. 以两个银行账户之间的转账行为为例,来说明如何实现多文档间的事务操作. ...