本文首发于cdream的个人博客,点击获得更好的阅读体验!

欢迎转载,转载请注明出处。

前段时间公司书架多了一本《Java8 实战》,毕竟久闻lambda的大名,于是借来一阅。这一看,简直是惊为天人啊,lambda,stream,java8里简直是满脑子骚操作,看我的一愣一愣的。我甚至是第一次感觉到了什么叫优雅。

本文主要介绍java8中的流处理,看看java8是怎么愉快的玩耍集合的,让我们来一起感受java8的魅力吧!

我就随便举个例子,看看Stream有多优雅。

// 对苹果按颜色汇总并绩数量
Map<String, Long> appleCount = apples.stream()
.collect(groupingBy(Apple::getColor, counting()));
// 过滤掉颜色为黑色的苹果,并汇总好苹果的总金额
Double sum = apples.stream()
.filter(i->"black".equals(i.getColor()))
.collect(toList);

一、lambda表达式

虽然本文重点是stream,但是stream中需要传递lambda表达式,所以简单介绍一下lambda表达式。lambda表达式其实就是匿名函数(anonymous function),是指一类无需定义标识符的函数或子程序。

java中匿名函数的表现形式,只留下入参和方法体中的内容

// 普通函数
public void run(String s){
System.out.print(s+"哈哈");
}
// 我不要名字啦!!!
(s)->System.out.print(s+"哈哈")

诶,过去我们都用对象调方法的,你弄这个没名的东西啥时候用啊?

java中我们通过函数式接口来使用这种匿名函数。

函数式接口
1.java中只包含一个未实现方法的接口。其中可以有与Object中同名的方法和默认方法(java8中接口方法可以有默认实现)。
2.java中函数式接口使用@FunctionalInterface进行注解。Runnable、Comparator都是函数式接口。
3.java.util.function包下为我们提供很多常用的函数式接口,例如Function等。

用法举例:

// 实现Runnable中的run方法,替代匿名内部类。
Runnable r = ()->System.out.print("哈哈");
// 作为参数传递。
new Thread(()-> System.out.println("haha")).start(); ArrayList<Apple> list = new ArrayList<>();
list.forEach(i-> System.out.println(i.getWeight())); // 简化策略模式
public static List<Apple> filterApples(List<Apple> inventory,ApplePredicate p){
List<Apple> apples = new ArrayList<>();
for(Apple apple : inventory){
if(p.test(apple)){
apples.add(apple);
}
}
return apples;
}
public class BigApple implement ApplePredicate{
@Override
public boolean test(Apple a){
if(a.getWeight>10){
return a
}
}
}
// 这是个简单的策略模式,根据用户的需要,创建不同的接口ApplePredicate实现类,调用时传入不同的实现类就可以,但问题是如果需求过多,创建的实现类也会很多,过于臃肿不方便管理。
xx.filterApple(inventory,new BigApple);
// 使用lambda表达式,不在需要创建BigApple类
xx.filterApple(inventory,i->(i.getWeight>10));

使用lambda表达式可以简化大量的模板代码,并且可以向方法直接传递代码。

总之

方法出参入参来自函数式接口
//入参s,返回void
(s)->System.out.println(s);
//入参空,返回void
()->System.out.print("haha");
//入参i,返回i+1
i->i+1
//后面写代码块
apple->{if(apple.getWeiht>5) return "BIG";
else return "small";
}

好了,不多啰嗦了,如果感兴趣推荐下面的文章或《Java8实战》的前三章。

二、Stream

流是什么?

Java API的新成员,它允许你使用声明式方式处理数据集合(类似sql,通过查询语句表达,而不是临时编写一个实现)。

如果有人说lambda表达式不易于理解,那还勉强可以接受(其实过于复杂的lambda缺失不好阅读,但通常lambda不会做太复杂的实现),但流真的非常的易懂易用。这个语法糖真的是甜死了。

注意事项:
1.流只能使用一次,遍历结束就代表这个流被消耗掉了
2.流对集合的操作属于内部迭代,是流帮助我们操作,而不是外部迭代
3.流操作包含:数据源,中间操作链,终端操作三个部分。

基础流操作

List<Double> collect = list.stream()
// 过滤掉黑色的苹果
.filter(i -> "black".equals(i.getColor()))
// 让苹果按照重量个价格排序
.sorted(Comparator.comparing(Apple::getWeight)
.thenComparing(i->i.getPrice()))
// 筛选掉重复的数据
.distinct()
// 只要苹果的价格
.map(Apple::getPrice)
// 只留下前两条数据
.limit(2)
// 以集合的形式返回
.collect(toList());
// 循环打印列表中元素
list.forEach(i->System.out.print(i));

Apple::getPrince<=>i -> i.getPrince()可以看做是仅涉及单一方法的语法糖,效果与lambda表达式相同,但可读性更好。

同理

下面列表为常见操作

中间

操作 类型 作用 函数描述 函数
filter 中间 过滤 T -> boolean Predicate
sorted 中间 排序 (T,T)->int Comparator
map 中间 映射 T->R Function<T,R>
limit 中间 截断
distinct 中间 去重,根据equals方法
skip 中间 跳过前n个元素

终端

操作 类型 作用
forEach 终端 消费流中的每个元素,使用lambda进行操作
count 终端 返回元素个数,long
collect 终端 将流归约成一个集合,如List,Map甚至是Integer

筛选与切片

List<String> strings = Arrays.asList("Hello", "World");
List<String> collect1 = strings.stream()
// String映射成String[]
.map(i -> i.split(""))
// Arrays::Stream 数据数组,返回一个流String[]->Stream<String>
// flatMap各数组并不分别映射成一个流,而是映射成流的内容 Stream<String>->Stream
.flatMap(Arrays::stream)
.collect(toList());
System.out.println(collect);
----->输出 [H, e, l, l, o, W, o, r, l, d]

归约操作reduce

List<Integer> integers = Arrays.asList(12, 3, 45, 3, 2,-1);
// 有初始值的叠加操作
Integer reduce = integers.stream().reduce(3, (i, j) -> i + j);
Integer reduce2 = integers.stream().reduce(5, (x, y) -> x < y ? x : y);
// 无初始值的叠加操作
Optional<Integer> reduce1 = integers.stream().reduce((i, j) -> i + j);
// 无初始值的最大值
Optional<Integer> reduce4 = integers.stream().reduce(Integer::min);
// 无初始值的最大值
Optional<Integer> reduce5 = integers.stream().reduce(Integer::max);
// 求和
Optional<Integer> reduce6 = integers.stream().reduce(Integer::sum);

reduce做的事情是取两个数进行操作,结果返回取下一个数操作,以次类推。

Optional是java8引入的新类,避免造成空指针异常,在集合为空时,结果会包在Optional中,可以用isPresent()方法来判断是否为空值。

无初始值的情况下可能为空,故返回Optional

中间

操作 类型 作用 函数描述 函数
flatmap 中间 使通过的流返回内容 T -> boolean Predicate

终端

操作 类型 作用
anyMatch 终端 返回boolean,判断是否有符合条件内容
noneMatch 终端 返回boolean,判断是否无符合条件内容
allMatch 终端 返回boolean,判断是全为符合条件内容
findAny 终端 Optional,随机找一个元素返回
findFirst 终端 Optional,返回第一个元素
reduce 终端 Optional (T,T)->T 归约操作

数值流

包装类型的各种操作都会有拆箱操作和装箱操作,严重影响性能。所以Java8为我们提供了原始数值流。

// 数值流求平均值
OptionalDouble average = apples.stream()
.mapToDouble(Apple::getPrice)
.average();
// 数值流求和
OptionalDouble average = apples.stream()
.mapToDouble(Apple::getPrice)
.sum();
// 数值流求最大值,没有则返回2
double v = apples.stream()
.mapToDouble(Apple::getPrice)
.max().orElse(2);
// 生成随机数
IntStream s = IntStream.rangeClosed(1,100);

下面列表为常见数值流操作操作

中间

操作 类型 作用
rangeClosed(1,100) 中间 生成随机数(1,100]
range(1,100) 中间 生成随机数(1,100)
boxed() 中间 包装成一般流
mapToObj 中间 返回为对象流
mapToInt 中间 映射为数值流

终端,终端操作与List一般流类似

构建流

  1. 值创建

    Stream<String> s = Stream.of("java","python");
  2. 数组创建

    int[]  i = {2,3,4,5};
    Stream<int> = Arrays.stream(i);
  3. 由文件生成,NIO API已经更新,以便利用Stream API

    Stream<String> s = Files.lines(Paths.get("data.txt"),Charset.defaultCharset());
  4. 由函数创建流:无限流

    // 迭代
    Stream.iterate(0,n->n+2)
    .limit(10)
    .forEach(System.out::println);
    // 生成,需要传递实现Supplier<T>类型的Lambda提供的新值
    Stream.generate(Math.random)
    .limit(5)
    .forEach(System.out::println);

三、总结

至此,本文讲述了常见的流操作,目前排序、筛选、求和、归约等大多数操作我们都能实现了。与过去相比,操作集合变的简单多了,代码也变的更加简练明了。

目前Vert.x,Spring新出的WebFlux都通过lambda表达式来简化代码,不久的将来,非阻塞式框架的大行其道时,lambda表达式必将变的更加重要!

至于开篇见到的分组!!!下篇文章见~


参考资料:

  1. Java8 实战,Raoul-Gabriel Urma,Mario Fusco,Alan Mycroft

9102年了,你还在用for循环操作集合?的更多相关文章

  1. 都9102年了,还不会Docker?10分钟带你从入门操作到实战上手

    Docker简述 Docker是一种OS虚拟化技术,是一个开源的应用容器引擎.它可以让开发者将应用打包到一个可移植的容器中,并且该容器可以运行在几乎所有linux系统中(Windows10目前也原生支 ...

  2. 还在用迭代器处理集合吗?试试Stream,真香

    前言 上一篇博客一文带你深入了解 Lambda 表达式和方法引用我给大家介绍了 Java8 函数式特性中的 Lambda,这篇文章我将继续讨论 stream 流的用法 声明:本文首发于博客园,作者:后 ...

  3. 都2019年了,还问GET和POST的区别

    摘要: 对比GET与POST. 原文:都9102年了,还问GET和POST的区别 作者:程淇铭 Fundebug经授权转载,版权归原作者所有. 1. 前言 最近看了一些同学的面经,发现无论什么技术岗位 ...

  4. 运用node真机调试移动web项目

    很多时候我们对移动端进行测试的时候,有pc端的测试,也有真机上的测试,pc的测试就不多说了,因为其实基本上大家都懂的.真机测试上也有几种方法,这里就推荐三种: 移动端真机调试方法 chrome真机调试 ...

  5. 面试神体验之:get和post的区别

    由于本文是用markdown在本地编辑的,粘贴到本地的时候出现了一些页面bug,所以只好贴进代码里面,一些链接失效,望见谅 Get和POST的区别 都9102年了,你们还在问get和post的区别?是 ...

  6. 使用Windows的Linux子系统搭建嵌入式开发环境

      亲,都9102年了,还在用VMware跑嵌入式交叉编译链吗?   北京时间2019年6月13日,Windows 10发布预览版本18917.版本的主要功能是Linux子系统(windows sub ...

  7. Markdown 标记语言指北 - 源码

    这是上一篇博客的源代码. 这是班刊约稿的一篇文章. 全文约6000字, 预计需要 60 分钟读完. # Markdown 标记语言指北 #### TOC 1. [什么是 Markdown?](#%E4 ...

  8. Markdown 标记语言指北

    这是班刊约稿的一篇文章. 全文约6000字, 预计需要 60 分钟读完. Markdown 标记语言指北 TOC 什么是 Markdown? Markdown 可以用来干什么? 第一步? 一些专业一点 ...

  9. DAY 1模拟赛

    DAY1 杨溢鑫小姐姐出题 好毒瘤啊 T1 低仿机器人 (robo,1s,64M) 题目描述 自从 Dji 推出 robomaster S1 机器人过后,小文就一直缠着爸爸想要一个机器人.没想到爸爸最 ...

随机推荐

  1. jQuery小案例

    Jquery例子1_占位符使用需求: 点击第一个按钮后 自动去check 后面是否有按钮没有选中, 如有则提示错误消息. <html> <head> <script ty ...

  2. 关于WordCount的作业

    一.开发者:201631062418 二.代码地址:https://gitee.com/YsuLIyan/WordCount 三.作业地址:https://edu.cnblogs.com/campus ...

  3. 201621123018《Java程序设计》第6周学习报告

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图或相关笔记,对面向对象思想进行一个总结. 2. 书面作业 1. clone方法 1.1 在 ...

  4. Python 模块之wxpython 的应用

    第一个应用程序:“Hello World” 作为传统,我们首先将要写一个小的“Hello World”程序,下面是他的代码: #!/usr/bin/env python import wx app = ...

  5. 《Python自动化运维之路》 业务服务监控(二)

    文件内容差异对比方法 使用diffie模块实现文件内容差异对比.dmib作为 Python的标准库模块,无需安装,作用是对比文本之间的差异,且支持输出可读性比较强的HTML文档,与 Linux下的di ...

  6. FileAttributeView出现空指针异常原因分析

    问题?   Java7新增了关于文件属性信息的一些新特性,通过java.nio.file.*包下面的类可以实现设置或者读取文件的元数据信息(比如最后修改时间,创建时间,文件大小,是否为目录等等).尤其 ...

  7. 使用Flexbox:新旧语法混用实现最佳浏览器兼容

    Flexbox非常的棒,肯定是未来布局的一种主流.在过去的几年这之中,语法改变了不少,这里有一篇“旧”和“新”新的语法区别教程(如果你对英文不太感兴趣,可以移步阅读中文版本).但是,如果我们把Flex ...

  8. JavaScript 那些不经意间发生的数据类型自动转换

    JavaScript可以自由的进行数据类型转换,也提供了多种显式转换的方式.但是更多的情况下,是由JavaScript自动转换的,当然这些转换遵循着一定的规则,了解数据类型自由转换的规则是非常必要的. ...

  9. xamarin 编译出现Xamarin.Build.Forms.Tasks.GetTaskAbi 无法加载的错误解决方法

    最新升级最新的vs2017后发现编译xamarin forms 会出现错误 Xamarin.Forms.Build.Tasks.GetTasksAbi task could not be loaded ...

  10. Oracle数据库中NARCHAR转换成NUMBER类型

    1.修改后的代码: public void addDirectorActorKeyword(long idStart, long idEnd) { SeriesMgr seriesMgr = new ...