前面的一系列博客中,我们都是从一个集合中拿到一个流,但是有时候需要执行反操作,就是从流中获得集合。实际编码中,当我们处理完流后,我们通常想查看下结果,而不是将他们聚合成一个值。我们可以调用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. Hadoop 新生报道(四) WordCount

         WordCount是hadoop里hello word级的第一个程序,作为一个萌新,我也来跑一跑这个,附带针对新人的说明.      所谓WordCount,就是统计一个或几个文档中相同的单 ...

  2. Box布局

    import sys from PyQt4 import QtCore, QtGui class MainWindow(QtGui.QWidget): def __init__(self, paren ...

  3. Linux常见命令(系统命令)

    1.查看主机名hostname 2.修改主机名(重启后无效)hostname hadoop 3.修改主机名(重启后永久生效)vi /etc/sysconfig/network[hostname=had ...

  4. chrome调试工具高级不完整使用指南(实战二)

    3.3 给页面添加测试脚本 在现实的工作中,我们往往会遇到一些问题在线上就会触发然后本地就触发不了的问题.或者是,要给某个元素写一个测试脚本.这个时候如果是浏览器有提供一个添加脚本的功能的话,那么我们 ...

  5. [原创]在Centos7.2上源码安装PHP、Nginx、Zentao禅道

    版本 操作系统:CentOS Linux release 7.2.1511 (Core) PHP:5.6.33 Nginx:1.12.2 MySQL:5.6.38(192.168.1.103的Wind ...

  6. 一个通用的makefile(一)

    最近在编写Android编译系统时,需要遍历每一个目录下每一个文件夹下的makefile,网上的方法有些繁琐 :就直接贴上自己遍历子目录深度为1:(for  temporary)(之后会继续更新) 下 ...

  7. Struts2中访问web元素的四种方式

    Struts2中访问web元素的四种方式如下: 通过ActionContext来访问Map类型的request.session.application对象. 通过实现RequestAware.Sess ...

  8. jdbc+servlet+jsp实现登录验证

    基础知识准备:sql的增删改查. 新增:insert into 表名称(字段名.....)values(字段名....). 修改:update 表名称 set  字段名="新值" ...

  9. python 序列话模块 常用

    什么是序列化? 我们把对象(变量)从内存中变成可存储或传输的过程称之为序列化,在Python中叫pickling,在其他语言中也被称之为serialization,marshalling,flatte ...

  10. jq.paginator分页插件稍加修改

    样式一: 样式二: 此分页功能在jq.paginator分页插件上稍加修改. 必加模板html: <div id="jqPaginator"> <div id=& ...