[TOC]

0 Stream简介

  • 家庭住址 :java.util.stream.Stream<T>

  • 出生年月:Java8问世的时候他就来到了世上

  • 主要技能:那可以吹上三天三夜了……

  • 主要特征

    • 不改变输入源

    • 中间的各种操作是lazy的(惰性求值、延迟操作)

    • 只有当开始消费流的时候,流才有意义

    • 隐式迭代

  • ……

总体感觉,Stream相当于一个进化版的Iterator。Java8源码里是这么注释的:

A sequence of elements supporting sequential and parallel aggregate operations

可以方便的对集合进行遍历、过滤、映射、汇聚、切片等复杂操作。最终汇聚成一个新的Stream,不改变原始数据。并且各种复杂的操作都是lazy的,也就是说会尽可能的将所有的中间操作在最终的汇聚操作一次性完成。

比起传统的对象和数据的操作,Stream更专注于对流的计算,和传说中的函数式编程有点类似。

他具体进化的多牛逼,自己体验吧。

给一组输入数据:

List<Integer> list = Arrays.asList(1, null, 3, 1, null, 4, 5, null, 2, 0);

求输入序列中非空奇数之和,并且相同奇数算作同一个。

  • 在lambda还在娘胎里的时候,为了实现这个功能,可能会这么做

int s = 0;
// 先放在Set里去重
Set<Integer> set = new HashSet<>(list);
for (Integer i : set) {if (i != null && (i & 1) == 0) {s += i;}
}
System.out.println(s);
  • lambdaStream双剑合璧之后:

int sum = list.stream().filter(e -> e != null && (e & 1) == 1).distinct().mapToInt(i -> i).sum();

1 获取Stream

  • 从lambda的其他好基友那里获取Stream

从1.8开始,接口中也可以存在 default 修饰的方法了。

java.util.Collection<E> 中有如下声明:

public interface Collection<E> extends Iterable<E> {// 获取普通的流default Stream<E> stream() {return StreamSupport.stream(spliterator(), false);}// 获取并行流default Stream<E> parallelStream() {return StreamSupport.stream(spliterator(), true);}
}

java.util.Arrays中有如下声明:

    public static <T> Stream<T> stream(T[] array) {return stream(array, 0, array.length);}public static IntStream stream(int[] array) {return stream(array, 0, array.length);}// 其他类似的方法不再一一列出

示例

List<String> strs = Arrays.asList("apache", "spark");
Stream<String> stringStream = strs.stream();IntStream intStream = Arrays.stream(new int[] { 1, 25, 4, 2 });
  • 通过Stream接口获取

Stream<String> stream = Stream.of("hello", "world");
Stream<String> stream2 = Stream.of("haha");
Stream<HouseInfo> stream3 = Stream.of(new HouseInfo[] { new HouseInfo(), new HouseInfo() });Stream<Integer> stream4 = Stream.iterate(1, i -> 2 * i + 1);Stream<Double> stream5 = Stream.generate(() -> Math.random());

注意:Stream.iterate()Stream.generate()生成的是无限流,一般要手动limit

2 转换Stream

流过滤、流切片

这部分相对来说还算简单明了,看个例子就够了

// 获取流
Stream<String> stream = Stream.of(//null, "apache", null, "apache", "apache", //"github", "docker", "java", //"hadoop", "linux", "spark", "alifafa");stream// 去除null,保留包含a的字符串.filter(e -> e != null && e.contains("a"))//.distinct()// 去重,当然要有equals()和hashCode()方法支持了.limit(3)// 只取满足条件的前三个.forEach(System.out::println);// 消费流

map/flatMap

Stream的map定义如下:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

也就是说,接收一个输入(T:当前正在迭代的元素),输出另一种类型(R)。

Stream.of(null, "apache", null, "apache", "apache", //"hadoop", "linux", "spark", "alifafa")//.filter(e -> e != null && e.length() > 0)//.map(str -> str.charAt(0))//取出第一个字符.forEach(System.out::println);

sorted

排序也比较直观,有两种:

// 按照元素的Comparable接口的实现来排序
Stream<T> sorted();// 指定Comparator来自定义排序
Stream<T> sorted(Comparator<? super T> comparator);

示例:

List<HouseInfo> houseInfos = Lists.newArrayList(//new HouseInfo(1, "恒大星级公寓", 100, 1), //new HouseInfo(2, "汇智湖畔", 999, 2), //new HouseInfo(3, "张江汤臣豪园", 100, 1), //new HouseInfo(4, "保利星苑", 23, 10), //new HouseInfo(5, "北顾小区", 66, 23), //new HouseInfo(6, "北杰公寓", null, 55), //new HouseInfo(7, "保利星苑", 77, 66), //new HouseInfo(8, "保利星苑", 111, 12)//
);houseInfos.stream().sorted((h1, h2) -> {if (h1 == null || h2 == null)return 0;if (h1.getDistance() == null || h2.getDistance() == null)return 0;int ret = h1.getDistance().compareTo(h2.getDistance());if (ret == 0) {if (h1.getBrowseCount() == null || h2.getBrowseCount() == null)return 0;return h1.getBrowseCount().compareTo(h2.getBrowseCount());}return ret;
});

3 终止/消费Stream

条件测试、初级统计操作

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);// 是不是所有元素都大于零
System.out.println(list.stream().allMatch(e -> e > 0));
// 是不是存在偶数
System.out.println(list.stream().anyMatch(e -> (e & 1) == 0));
// 是不是都不小于零
System.out.println(list.stream().noneMatch(e -> e < 0));// 找出第一个大于等于4的元素
Optional<Integer> optional = list.stream().filter(e -> e >= 4).findFirst();
// 如果存在的话,就执行ifPresent中指定的操作
optional.ifPresent(System.out::println);// 大于等于4的元素的个数
System.out.println(list.stream().filter(e -> e >= 4).count());
// 获取最小的
System.out.println(list.stream().min(Integer::compareTo));
// 获取最大的
System.out.println(list.stream().max(Integer::compareTo));
// 先转换成IntStream,max就不需要比较器了
System.out.println(list.stream().mapToInt(i -> i).max());

reduce

这个词不知道怎么翻译,有人翻译为 规约汇聚

反正就是将经过一系列转换后的流中的数据最终收集起来,收集的同时可能会反复 apply 某个 reduce函数

reduce()方法有以下两个重载的变体:

// 返回的不是Optional,因为正常情况下至少有参数identity可以保证返回值不会为null
T reduce(T identity, BinaryOperator<T> accumulator);<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);

示例:

// 遍历元素,反复apply (i,j)->i+j的操作
Integer reduce = Stream.iterate(1, i -> i + 1)//1,2,3,...,10,....limit(10)//.reduce(0, (i, j) -> i + j);//55Optional<Integer> reduce2 = Stream.iterate(1, i -> i + 1)//.limit(10)//.reduce((i, j) -> i + j);

collect

该操作很好理解,顾名思义就是将Stream中的元素collect到一个地方。

  • 最常规(最不常用)的collect方法

// 最牛逼的往往是最不常用的,毕竟这个方法理解起来太过复杂了
<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);

Collector接口(他不是函数式接口,没法使用lambda)的关键代码如下:

public interface Collector<T, A, R> {/****/Supplier<A> supplier();/*** */BiConsumer<A, T> accumulator();/*** */BinaryOperator<A> combiner();/****/Function<A, R> finisher();/*** */Set<Characteristics> characteristics();}

先来看一个关于三个参数的collect()方法的例子,除非特殊情况,不然我保证你看了之后这辈子都不想用它……

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
ArrayList<Integer> ret1 = numbers.stream()//.map(i -> i * 2)// 扩大两倍.collect(//() -> new ArrayList<Integer>(), //参数1(list, e) -> list.add(e), //参数2(list1, list2) -> list1.addAll(list2)//参数3
);/**** <pre>* collect()方法的三个参数解释如下:* 1. () -> new ArrayList<Integer>() *         生成一个新的用来存储结果的集合* 2. (list, e) -> list.add(e)*         list:是参数1中生成的新集合*         e:是Stream中正在被迭代的当前元素*         该参数的作用就是将元素添加到新生成的集合中* 3. (list1, list2) -> list1.addAll(list2)*         合并集合* </pre>***/ret1.forEach(System.out::println);

不使用lambda的时候,等价的代码应该是这个样子的……

List<Integer> ret3 = numbers.stream()//.map(i -> i * 2)// 扩大两倍.collect(new Supplier<List<Integer>>() {@Overridepublic List<Integer> get() {// 只是为了提供一个集合来存储元素return new ArrayList<>();}}, new BiConsumer<List<Integer>, Integer>() {@Overridepublic void accept(List<Integer> list, Integer e) {// 将当前元素添加至第一个参数返回的容器中list.add(e);}}, new BiConsumer<List<Integer>, List<Integer>>() {@Overridepublic void accept(List<Integer> list1, List<Integer> list2) {// 合并容器list1.addAll(list2);}});ret3.forEach(System.out::println);

是不是被恶心到了……

同样的,用Java调用spark的api的时候,如果没有lambda的话,比上面的代码还恶心……

顺便打个免费的广告,可以看看本大侠这篇使用各种版本实现的Spark的HelloWorld: http://blog.csdn.net/hylexus/...,来证明一下有lambda的世界是有多么幸福……

不过,当你理解了三个参数的collect方法之后,可以使用构造器引用和方法引用来使代码更简洁:

ArrayList<Integer> ret2 = numbers.stream()//.map(i -> i * 2)// 扩大两倍.collect(//ArrayList::new, //List::add, //List::addAll//
);ret2.forEach(System.out::println);

Collectors工具的使用(高级统计操作)

上面的三个和一个参数的collect()方法都异常复杂,最常用的还是一个参数的版本。但是那个Collector自己实现的话还是很恶心。

还好,常用的Collect操作对应的Collector都在java.util.stream.Collectors 中提供了。很强大的工具……

以下示例都是对该list的操作:

List<HouseInfo> houseInfos = Lists.newArrayList(//new HouseInfo(1, "恒大星级公寓", 100, 1), // 小区ID,小区名,浏览数,距离new HouseInfo(2, "汇智湖畔", 999, 2), //new HouseInfo(3, "张江汤臣豪园", 100, 1), //new HouseInfo(4, "保利星苑", 111, 10), //new HouseInfo(5, "北顾小区", 66, 23), //new HouseInfo(6, "北杰公寓", 77, 55), //new HouseInfo(7, "保利星苑", 77, 66), //new HouseInfo(8, "保利星苑", 111, 12)//
);

好了,开始装逼之旅 ^_^ ……

  • 提取小区名

// 获取所有小区名,放到list中
List<String> ret1 = houseInfos.stream().map(HouseInfo::getHouseName).collect(Collectors.toList());
ret1.forEach(System.out::println);// 获取所有的小区名,放到set中去重
// 当然也可先distinct()再collect到List中
Set<String> ret2 = houseInfos.stream().map(HouseInfo::getHouseName).collect(Collectors.toSet());
ret2.forEach(System.out::println);// 将所有的小区名用_^_连接起来
// 恒大星级公寓_^_汇智湖畔_^_张江汤臣豪园_^_保利星苑_^_北顾小区_^_北杰公寓_^_保利星苑_^_保利星苑
String names = houseInfos.stream().map(HouseInfo::getHouseName).collect(Collectors.joining("_^_"));
System.out.println(names);// 指定集合类型为ArrayList
ArrayList<String> collect = houseInfos.stream().map(HouseInfo::getHouseName).collect(Collectors.toCollection(ArrayList::new));
  • 最值

// 获取浏览数最高的小区
Optional<HouseInfo> ret3 = houseInfos.stream()//.filter(h -> h.getBrowseCount() != null)// 过滤掉浏览数为空的.collect(Collectors.maxBy((h1, h2) -> Integer.compare(h1.getBrowseCount(), h2.getBrowseCount())));
System.out.println(ret3.get());// 获取最高浏览数
Optional<Integer> ret4 = houseInfos.stream()//.filter(h -> h.getBrowseCount() != null)// 去掉浏览数为空的.map(HouseInfo::getBrowseCount)// 取出浏览数.collect(Collectors.maxBy(Integer::compare));// 方法引用,比较浏览数
System.out.println(ret4.get());
  • 总数、总和

// 获取总数
// 其实这个操作直接用houseInfos.size()就可以了,此处仅为演示语法
Long total = houseInfos.stream().collect(Collectors.counting());
System.out.println(total);// 浏览数总和
Integer ret5 = houseInfos.stream()//.filter(h -> h.getBrowseCount() != null)// 过滤掉浏览数为空的.collect(Collectors.summingInt(HouseInfo::getBrowseCount));
System.out.println(ret5);// 浏览数总和
Integer ret6 = houseInfos.stream()//.filter(h -> h.getBrowseCount() != null)// 过滤掉浏览数为空的.map(HouseInfo::getBrowseCount).collect(Collectors.summingInt(i -> i));
System.out.println(ret6);// 浏览数总和
int ret7 = houseInfos.stream()//.filter(h -> h.getBrowseCount() != null)// 过滤掉浏览数为空的.mapToInt(HouseInfo::getBrowseCount)// 先转换为IntStream后直接用其sum()方法.sum();
System.out.println(ret7);
  • 均值

// 浏览数平均值
Double ret8 = houseInfos.stream()//.filter(h -> h.getBrowseCount() != null)// 过滤掉浏览数为空的.collect(Collectors.averagingDouble(HouseInfo::getBrowseCount));
System.out.println(ret8);// 浏览数平均值
OptionalDouble ret9 = houseInfos.stream()//.filter(h -> h.getBrowseCount() != null)// 过滤掉浏览数为空的.mapToDouble(HouseInfo::getBrowseCount)// 先转换为DoubleStream后直接用其average()方法.average();
System.out.println(ret9.getAsDouble());
  • 统计信息

// 获取统计信息
DoubleSummaryStatistics statistics = houseInfos.stream()//.filter(h -> h.getBrowseCount() != null).collect(Collectors.summarizingDouble(HouseInfo::getBrowseCount));
System.out.println("avg:" + statistics.getAverage());
System.out.println("max:" + statistics.getMax());
System.out.println("sum:" + statistics.getSum());
  • 分组

// 按浏览数分组
Map<Integer, List<HouseInfo>> ret10 = houseInfos.stream()//.filter(h -> h.getBrowseCount() != null)// 过滤掉浏览数为空的.collect(Collectors.groupingBy(HouseInfo::getBrowseCount));
ret10.forEach((count, house) -> {System.out.println("BrowseCount:" + count + " " + house);
});// 多级分组
// 先按浏览数分组,二级分组用距离分组
Map<Integer, Map<String, List<HouseInfo>>> ret11 = houseInfos.stream()//.filter(h -> h.getBrowseCount() != null && h.getDistance() != null)//.collect(Collectors.groupingBy(HouseInfo::getBrowseCount,Collectors.groupingBy((HouseInfo h) -> {if (h.getDistance() <= 10)return "较近";else if (h.getDistance() <= 20)return "近";return "较远";})));//结果大概长这样
ret11.forEach((count, v) -> {System.out.println("浏览数:" + count);v.forEach((desc, houses) -> {System.out.println("\t" + desc);houses.forEach(h -> System.out.println("\t\t" + h));});
});
/***** <pre>*  浏览数:66较远HouseInfo [houseId=5, houseName=北顾小区, browseCount=66, distance=23]浏览数:100较近HouseInfo [houseId=1, houseName=恒大星级公寓, browseCount=100, distance=1]HouseInfo [houseId=3, houseName=张江汤臣豪园, browseCount=100, distance=1]浏览数:999较近HouseInfo [houseId=2, houseName=汇智湖畔, browseCount=999, distance=2]浏览数:77较远HouseInfo [houseId=6, houseName=北杰公寓, browseCount=77, distance=55]HouseInfo [houseId=7, houseName=保利星苑, browseCount=77, distance=66]浏览数:111近HouseInfo [houseId=8, houseName=保利星苑, browseCount=111, distance=12]较近HouseInfo [houseId=4, houseName=保利星苑, browseCount=111, distance=10]* * </pre>* ****/
  • 分区

// 按距离分区(两部分)
Map<Boolean, List<HouseInfo>> ret12 = houseInfos.stream()//.filter(h -> h.getDistance() != null)//.collect(Collectors.partitioningBy(h -> h.getDistance() <= 20));
/***** <pre>*  较远HouseInfo [houseId=5, houseName=北顾小区, browseCount=66, distance=23]HouseInfo [houseId=6, houseName=北杰公寓, browseCount=77, distance=55]HouseInfo [houseId=7, houseName=保利星苑, browseCount=77, distance=66]较近HouseInfo [houseId=1, houseName=恒大星级公寓, browseCount=100, distance=1]HouseInfo [houseId=2, houseName=汇智湖畔, browseCount=999, distance=2]HouseInfo [houseId=3, houseName=张江汤臣豪园, browseCount=100, distance=1]HouseInfo [houseId=4, houseName=保利星苑, browseCount=111, distance=10]HouseInfo [houseId=8, houseName=保利星苑, browseCount=111, distance=12]* * </pre>****/
ret12.forEach((t, houses) -> {System.out.println(t ? "较近" : "较远");houses.forEach(h -> System.out.println("\t\t" + h));
});Map<Boolean, Map<Boolean, List<HouseInfo>>> ret13 = houseInfos.stream()//.filter(h -> h.getDistance() != null)//.collect(Collectors.partitioningBy(h -> h.getDistance() <= 20,Collectors.partitioningBy(h -> h.getBrowseCount() >= 70))
);/****** <pre>*  较远浏览较少HouseInfo [houseId=5, houseName=北顾小区, browseCount=66, distance=23]浏览较多HouseInfo [houseId=6, houseName=北杰公寓, browseCount=77, distance=55]HouseInfo [houseId=7, houseName=保利星苑, browseCount=77, distance=66]较近浏览较少浏览较多HouseInfo [houseId=1, houseName=恒大星级公寓, browseCount=100, distance=1]HouseInfo [houseId=2, houseName=汇智湖畔, browseCount=999, distance=2]HouseInfo [houseId=3, houseName=张江汤臣豪园, browseCount=100, distance=1]HouseInfo [houseId=4, houseName=保利星苑, browseCount=111, distance=10]HouseInfo [houseId=8, houseName=保利星苑, browseCount=111, distance=12]* </pre>****/ret13.forEach((less, value) -> {System.out.println(less ? "较近" : "较远");value.forEach((moreCount, houses) -> {System.out.println(moreCount ? "\t浏览较多" : "\t浏览较少");houses.forEach(h -> System.out.println("\t\t" + h));});
});

java8-02-Stream-API相关推荐

  1. 【Java8新特性】关于Java8的Stream API,看这一篇就够了!!

    写在前面 Java8中有两大最为重要的改变.第一个是 Lambda 表达式:另外一个则是 Stream API(java.util.stream.*)  ,那什么是Stream API呢?Java8中 ...

  2. Java8的Stream API使用(笔记)

    个人转载仅作为笔记使用,如有任何问题可以到原文链接探讨,原文连接为:https://www.cnblogs.com/jimoer/p/10995574.html 前言 这次想介绍一下Java Stre ...

  3. Java8的Stream API使用

    前言 这次想介绍一下Java Stream的API使用,最近在做一个新的项目,然后终于可以从老项目的祖传代码坑里跳出来了.项目用公司自己的框架搭建完成后,我就想着把JDK版本也升级一下吧(之前的项目, ...

  4. java8之Stream API(提取子流和组合流)

    为什么80%的码农都做不了架构师?>>>    Stream.limit(n)会返回一个包含n个元素的新流(如果原始流的长度小于n,则会返回原始的流).例如: package jav ...

  5. java8新特性-lambda表达式和stream API的简单使用

    一.为什么使用lambda Lambda 是一个 匿名函数,我们可以把 Lambda表达式理解为是 一段可以传递的代码(将代码像数据一样进行传递).可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风 ...

  6. 学习笔记之-java8的新特性-函数式接口,lambda表达式,方法引用,Stream API,Optional类

    1.Lambda表达式 用匿名内部类的方法去创建多线程1.new Thread2.参数传递new Runnable3.重写run方法4.在run方法中去设置线程任务5.调用start问题:我们最终目标 ...

  7. 【java8新特性】——Stream API详解(二)

    一.简介 java8新添加了一个特性:流Stream.Stream让开发者能够以一种声明的方式处理数据源(集合.数组等),它专注于对数据源进行各种高效的聚合操作(aggregate operation ...

  8. Java8————Stream API

    引言 Java8 加入了java.util.stream包,这个包中的相关API将极大的增强容器对象对元素的操作能力. 它专注于对集合对象进行各种便利.高效的聚合操作,或大批量数据处理. Stream ...

  9. Java8新特性总结 -5.Stream API函数式操作流元素集合

    所有示例代码打包下载 : 点击打开链接 Java8新特性 : 接口新增默认方法和静态方法 Optional类 Lambda表达式 方法引用 Stream API - 函数式操作流元素集合 Date/T ...

  10. java outputstream api,Java8 stream API以及常用方法

    Java8引入了全新的Stream API,Stream API为Java编程提供了丰富的函数式编程操作. Stream API与Java提供的其他集合类型有本质的区别,具体如下: java.io的I ...

最新文章

  1. cnpm install -g generator-gulp-webapp yo gulp-webapp test-gulp-webapp
  2. linux远程桌面复制文件,SH,telnet,网络拷贝文件,putty,连接windows远程桌面终端,远程控制linux桌面...
  3. python字符串find函数-python字符串查找函数的用法详解
  4. day26 re正则表达式
  5. 九度OJ 朋友圈 并查集
  6. SAP programming language培训环境准备 index.html
  7. spring mvc学习(21):testparam请求参数和请求头表达式
  8. centos6设置静态IP
  9. 必背单词_初中英语:2000个必背单词(附音标)汇总,打印背完,3年考高分
  10. binwalk、foremost、dd隐藏文件分离
  11. 2020五一建模:C题 饲料混合加工问题 题解
  12. STM8S103之外部中断
  13. SQL语句多表连接查询语法
  14. linux igb网卡,网卡驱动程序igb ixgbe 介绍
  15. Spring Cloud Gateway源码解析
  16. bandzip屏蔽广告
  17. 给JAVA做个类VS的拖放式界面设计工具
  18. win10系统C盘出现感叹号及加密图标解除
  19. 蛋白质二级结构预测-Chou-Fasman预测方法
  20. 零基础Bootstrap入门教程(16)--模态框

热门文章

  1. Java 捕获 mybatis异常_3 springboot集成mybatis和全局异常捕获
  2. linux 搜索命令 历史,Linux 控制台神器:搜索历史命令 Ctrl + R ( ctrl + r to search the history command )...
  3. mysql租车管理系统_基于java实现租车管理系统
  4. c语言内容逆置程序设计,C语言程序设计练习题含程序及参考答案.docx
  5. php铺满,重复铺满水印 - Jun. - OSCHINA - 中文开源技术交流社区
  6. mysql 出现 quot_MYSQL 新版出现 Client does_mysql _ 搞代码
  7. mysql解压包安装linuex_CentOS下安装Apache,php,Mysql
  8. c语言程序设计移动字母,C语言程序设计模拟试题二(含答案)
  9. alert 标题_[SwiftUI 知识碎片] Button、Image 和 Alert
  10. 2011年计算机一级考试题,2011年计算机一级考试试题及答案