一、什么是流

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。

Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。

流看作在时间中分布的一组值。相反,集合则是空间(这里就是计算机内存)中分布的一组值,在一个时间点上全体存在——你可以使用迭代器来访问for-each循环中的内部成员。

Java 7 之前的处理:

List<Dish> lowCaloricDishes = new ArrayList<>();
for(Dish d: menu){
if(d.getCalories() < 400){
lowCaloricDishes.add(d);
}
}
Collections.sort(lowCaloricDishes, new Comparator<Dish>() {
public int compare(Dish d1, Dish d2){
return Integer.compare(d1.getCalories(), d2.getCalories());
}
});
List<String> lowCaloricDishesName = new ArrayList<>();
for(Dish d: lowCaloricDishes){
lowCaloricDishesName.add(d.getName());
}

Java 8 流处理:

import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
List<String> lowCaloricDishesName =
menu.stream()
.filter(d -> d.getCalories() < 400)
.sorted(comparing(Dish::getCalories))
.map(Dish::getName)
.collect(toList());

Stream Api的特性:

  • 声明性--更简洁,更易读
  • 可复合--更灵活
  • 可并行--性能更好
  • 只能遍历一遍

二、使用流

简单说,对 Stream 的使用就是实现一个 filter-map-reduce 过程,产生一个最终结果,或者导致一个副作用(side effect)。例如:

List<String> name = menu.stream()
.filter(dish -> dish.getCalories() > 300)
.map(Dish::getName)
.limit(3)
.collect(Collectors.toList());
  • filter:从流中排除某些元素
  • map:将元素转换为其他形式或提取消息
  • limit:截断流
  • collect:将流转换为其他形式

接下来,我们将开始学习Stream中的Api 使用技巧,接下来将会使用到实体类 -Dish,以及链表:

实体类:Dish

public class Dish {
private String name;
private boolean vegetarian;
private int calories;
private Type type; public enum Type {MEAT, FISH, OTHER} public Dish(String name, boolean vegetarian, int calories, Type type) {
this.name = name;
this.vegetarian = vegetarian;
this.calories = calories;
this.type = type;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public boolean isVegetarian() {
return vegetarian;
} public void setVegetarian(boolean vegetarian) {
this.vegetarian = vegetarian;
} public int getCalories() {
return calories;
} public void setCalories(int calories) {
this.calories = calories;
} public Type getType() {
return type;
} public void setType(Type type) {
this.type = type;
}
}

使用链表:

Arrays.asList(
new Dish("pork", false, 800, Dish.Type.MEAT),
new Dish("beef", false, 700, Dish.Type.MEAT),
new Dish("chicken", false, 400, Dish.Type.MEAT),
new Dish("french fries", true, 530, Dish.Type.OTHER),
new Dish("rice", true, 350, Dish.Type.OTHER),
new Dish("season fruit", true, 120, Dish.Type.OTHER),
new Dish("pizza", true, 550, Dish.Type.OTHER),
new Dish("prawns", false, 300, Dish.Type.FISH),
new Dish("salmon", false, 450, Dish.Type.FISH));

1、筛选 Filter

1.1 谓词筛选 filter

该操作会接受一个谓词(一个返回boolean的函数)作为参数,并返回一个包括所有符合谓词的元素的流。

示例代码:

List<Dish> vegatarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(Collectors.toList());

  

1.2 去重筛选 distinct()

流还支持一个叫作distinct的方法,它会返回一个元素各异(根据流所生成元素的 hashCode和equals方法实现)的流

示例代码:

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(integer -> integer % 2 == 0)
.distinct()
.forEach(System.out::println);

输出结果为:2,4(去重一个 2)

1.3 截短流 limit(n)

流支持limit(n)方法,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递 给limit

List<Dish> vegatarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.limit(3)
.collect(Collectors.toList());

1.4 跳过元素 skip(n)

流还支持skip(n)方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流

List<Dish> vegatarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.skip(2)
.collect(Collectors.toList());

2、映射 Map

流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”

2.1 对流中的每一个元素应用

List<String> dishNames = menu.stream()
.map(Dish::getName)
.collect(Collectors.toList());

2.2 流的扁平化

对于一张单词表, 如何返回一张列表, 列出里面各不相同的字符呢? 例如, 给定单词列表["Hello","World"],你想要返回列表["H","e","l", "o","W","r","d"]

List<String> uniqueCharacters = words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());

 

3、匹配查找 Match Find

3.1 AnyMatch

anyMatch方法可以回答“流中是否有一个元素能匹配给定的谓词”。返回结果为boolean数据类型,如果流中出现匹配项,返回True。

if (list.stream().anyMatch(Dish::isVegetarian)){
System.out.printf("The menu is (somewhat) vegetarian friendly!!");
}

3.2 AllMatch

AllMatch 用法和AnyMatch 相似,方法返回结果为“流中所有结果都符合判断规则”。

boolean flag = list.stream().allMatch(dish -> dish.getCalories() <1000);

3.3 NoneMatch

NoneMatch 与 AllMatch 恰恰相反,返回结果为“流中所有结果都不符合”。

boolean flag = list.stream().allMatch(dish -> dish.getCalories() <1000);

3.4 findAny

findAny方法将返回当前流中的任意元素。它可以与其他流操作结合使用。方法返回结果为 Optional<T>。

list.stream()
.filter(Dish::isVegetarian)
.findAny()
.ifPresent(d -> System.out.println(d.getName()));

3.5 findFirst

有些流有一个出现顺序(encounterorder)来指定流中项目出现的逻辑顺序(比如由List或排序好的数据列生成的流)。对于这种流,你可能想要找到第一个元素。为此有一个finFirst 方法,它的工作方式类似于findany。

someNumbers.stream()
.map(x -> x * x)
.filter(x -> x % 3 == 0)
.findFirst()
.ifPresent(System.out::println);

3.6 何时使用findFirst和findAny

你可能会想,为什么会同时有findFirst和findAny呢?答案是并行。找到第一个元素 在并行上限制更多。如果你不关心返回的元素是哪个,请使用findAny,因为它在使用并行流 时限制较少。

4.归约 Reduce

需要将流中所有元素反复结合起来,得到一个值,比如一个Integer。这样的查询可以被归类为约操作 (将流归约成一个值)。

元素求和

//Type 1
int result = list.stream()
.reduce(0, Integer::sum);
System.out.println(result); //Type 2
list.stream()
.reduce(Integer::sum)
.ifPresent(System.out::println);

最大值和最小值

//Max
list.stream()
.reduce(Integer::max)
.ifPresent(System.out::println); //Min
list.stream()
.reduce(Integer::min)
.ifPresent(System.out::println);

三、总结

github 地址:https://github.com/jaycekon/StreamDemo

  • 流是“从支持数据处理操作的源生成的一系列元素”
  • 流利用内部迭代:迭代通过filter、map、sorted等操作被抽象掉了。
  • 流操作有两类:中间操作和终端操作。
  • filter和map等中间操作会返回一个流,并可以链接在一起。可以用它们来设置一条流 水线,但并不会生成任何结果。
  • forEach和count等终端操作会返回一个非流的值,并处理流水线以返回结果。
  • 流中的元素是按需计算的。

Stream-快速入门Stream编程的更多相关文章

  1. 官方文档中文版!Spring Cloud Stream 快速入门

    本文内容翻译自官方文档,spring-cloud-stream docs,对 Spring Cloud Stream的应用入门介绍. 一.Spring Cloud Stream 简介 官方定义 Spr ...

  2. 快速入门函数式编程——以Javascript为例

    函数式编程是在不改变状态和数据的情况下使用表达式和函数来编写程序的一种编程范式.通过遵守这种范式,我们能够编写更清晰易懂.更能抵御bug的代码.这是通过避免使用流控制语句(for.while.brea ...

  3. COM编程快速入门

    COM编程快速入门 COM编程快速入门 http://www.vckbase.com/index.php/wv/1642   COM是一种跨应用和语言共享二进制代码的方法.与C++不同,它提倡源代码重 ...

  4. Haskell 函数式编程快速入门【草】

    什么是函数式编程 用常规编程语言中的函数指针.委托和Lambda表达式等概念来帮助理解(其实函数式编程就是Lambda演算延伸而来的编程范式). 函数式编程中函数可以被非常容易的定义和传递. Hask ...

  5. .Net Core WebAPI 基于Task的同步&异步编程快速入门

    .Net Core WebAPI 基于Task的同步&异步编程快速入门 Task.Result async & await 总结 并行任务(Task)以及基于Task的异步编程(asy ...

  6. Java Stream API入门篇

    本文github地址 你可能还没意识到Java对函数式编程的重视程度,看看Java 8加入函数式编程扩充多少类就清楚了.Java 8之所以费这么大功夫引入函数式编程,原因有二: 代码简洁,函数式编程写 ...

  7. [三]java8 函数式编程Stream 概念深入理解 Stream 运行原理 Stream设计思路

    Stream的概念定义   官方文档是永远的圣经~     表格内容来自https://docs.oracle.com/javase/8/docs/api/   Package java.util.s ...

  8. Scala并发编程【快速入门】

    1.简介 Scala的actor提供了一种基于事件的轻量级线程.只要使用scala.actors.Actor伴生对象的actor()方法,就可以创建一个actor.它接受一个函数值/闭包做参数,一创建 ...

  9. CUDA编程之快速入门

    CUDA(Compute Unified Device Architecture)的中文全称为计算统一设备架构.做图像视觉领域的同学多多少少都会接触到CUDA,毕竟要做性能速度优化,CUDA是个很重要 ...

  10. R语言编程艺术(1)快速入门

    这本书与手上其他的R语言参考书不同,主要从编程角度阐释R语言,而不是从统计角度.因为之前并没有深刻考虑这些,因此写出的代码往往是一条条命令的集合,并不像是“程序”,因此,希望通过学习这本书,能提高编程 ...

随机推荐

  1. (转)rpm安装和卸载软件

    场景:在Linux中经常需要安装一些rpm软件,但是有时候安装失误就需要卸载这些软件包. 1 过程记录 1.1 安装 rpm -i 需要安装的包文件名 举例如下: rpm -i example.rpm ...

  2. html中ul元素水平排列问题

    <!DOCTYPE html> <html> <head> <style> #pic_list { display:block; white-space ...

  3. JAVA字符串操作 (转)

    JAVA字符串操作 原帖地址:http://blog.163.com/hn_myj@126/blog/static/50555635200861133942947/ 参考:http://blog.cs ...

  4. Mac下安装 MongoDB

    Mac 下安装 MongoDB 一般有两种方法,一种是通过源码安装,一种是直接使用 homebrew ,个人推荐使用 homebrew ,简单粗暴. 1.安装 homebrew : /usr/bin/ ...

  5. matrix()方法与translate()、scale()、rotate()、skew()方法的关系

    2D变换方法translate().scale().rotate().skew()与matrix()的关系举例介绍. 一.介绍 2D变换方法: translate():根据左(X轴)和顶部(Y轴)位置 ...

  6. java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-【费元星Q9715234】

    java+反射+多线程+生产者消费者模式+读取xml(SAX)入数据库mysql-[费元星Q9715234] 说明如下,不懂的问题直接我[费元星Q9715234] 1.反射的意义在于不将xml tag ...

  7. Windows环境下安装scikit-learn

    scikit-learn是Python的一个机器学习库,请按照以下步骤进行安装. 1.首先确保你的机器安装了Python并且配置好了环境变量. 2.安装pip 下载地址:https://pypi.py ...

  8. 双向循环链表(C语言描述)(三)

    代码清单 // linkedlist.h #ifndef __LINKEDLIST_H__ #define __LINKEDLIST_H__ #include <assert.h> #in ...

  9. NYOJ--714--Card Trick

    /* Name: NYOJ--714--Card Trick Author: shen_渊 Date: 19/04/17 19:35 Description: 早上训练的第六届河南省程序设计大赛的题, ...

  10. POJ 3254 Corn Fields:网格密铺类 状压dp

    题目链接:http://poj.org/problem?id=3254 题意: 给你一片n*m的耕地,你可以在上面种玉米.但是其中有一些地方是荒芜的,不能种植.并且种植玉米的地方不能相邻.问你在这片地 ...