Stream-快速入门Stream编程

一、什么是流

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等终端操作会返回一个非流的值,并处理流水线以返回结果。
  • 流中的元素是按需计算的。
时间: 2024-11-11 01:53:34

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

Node.js开发入门—Stream用法详解

Stream是Node.js中非常重要的一个模块,应用广泛.一个流是一个具备了可读.可写或既可读又可写能力的接口,通过这些接口,我们可以和磁盘文件.套接字.HTTP请求来交互,实现数据从一个地方流动到另一个地方的功能. 所有的流都实现了EventEmitter的接口,具备事件能力,通过发射事件来反馈流的状态.比如有错误发生时会发射"error"事件,有数据可被读取时发射"data"事件.这样我们就可以注册监听器来处理某个事件,达到我们的目的. Node.js定义了R

bash编程快速入门

首先,我们简单的介绍一下bash,bash是GNU计划编写的Unixshell,它是许多Linux平台上的内定shell,它提供了用户与系统的很好的交互,对于系统运维人员,bash的地位是举足轻重的,bash编程能很快处理日常的任务 bash入门,一个最简单的bash例子 #vim hello.sh #!/bin/bash #This is the first example of the bash #echo "Hello world" 下面,我们就这个简单的bash 脚本来介绍一下

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

这本书与手上其他的R语言参考书不同,主要从编程角度阐释R语言,而不是从统计角度.因为之前并没有深刻考虑这些,因此写出的代码往往是一条条命令的集合,并不像是"程序",因此,希望通过学习这本书,能提高编程效率,以及让自己更像是一个"程序员". 本文对应<R语言编程艺术>第1章:快速入门. 生成向量的函数c(),这里c表示"连接"(concatenate) 打印列表方式: print(listname) str(listname) 第一种将

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

什么是函数式编程 用常规编程语言中的函数指针.委托和Lambda表达式等概念来帮助理解(其实函数式编程就是Lambda演算延伸而来的编程范式). 函数式编程中函数可以被非常容易的定义和传递. Haskell 快速入门 概述 Haskell是一个按照纯函数式编程思想创造的语言,支持静态类型.类型推断.惰性处理(推迟计算).支持并发编程. 安装 从官方网站的下载页面 https://www.haskell.org/downloads 根据自己的操作系统选择. 第一次接触Haskell

《Python核心编程 》手记-快速入门

春节终于over了,回归充实的学习研究生活.打开久违的CSDN博客,看到官方推送的 『博客Markdown编辑器上线啦』,让我顿时有了写作的欲望,真是程序员的福利.之前阅读各种文章书籍,都是用MarkDownPad做的笔记,喜欢以及习惯于MarkDown简洁的语法. 总之各种方便.为了试试效果,将以前阅读<Python核心编程>的手记整理发上来,也当温习一遍. 第二章 快速入门 print语句中使用字符串格式操作符,实现字符替换功能. print "%s is %d" %(

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

Stream的概念定义   官方文档是永远的圣经~ 表格内容来自https://docs.oracle.com/javase/8/docs/api/   Package java.util.stream  一节部分原文内容的翻译 int sum = widgets.stream() .filter(b -> b.getColor() == RED) .mapToInt(b -> b.getWeight()) .sum(); 流操作被划分为中间和终端操作,并组合成流管道. 一条Stream管道由

bash shell编程快速入门教程

Shell 俗称壳(用来区别于核),是指"提供使用者使用界面"的命令解析器(软件).它类似于DOS下的command和后来的cmd.exe.它接收用户命令,然后调用相应的应用程序. 同时,Shell又是一种程序设计语言.作为命令语言,它交互式解释和执行用户输入的命令,或者自动地解释和执行预先设定好的一连串的命令.Shell不像C/C++等语言,它不需要编译就能执行.作为程序设计语言,Shell 定义了各种变量和参数,并提供了许多在高级语言中才具有的控制结构,包括循环和分支. UNIX系

CUDA编程之快速入门

CUDA(Compute Unified Device Architecture)的中文全称为计算统一设备架构.做图像视觉领域的同学多多少少都会接触到CUDA,毕竟要做性能速度优化,CUDA是个很重要的工具,CUDA是做视觉的同学难以绕过的一个坑,必须踩一踩才踏实.CUDA编程真的是入门容易精通难,具有计算机体系结构和C语言编程知识储备的同学上手CUDA编程应该难度不会很大.本文章将通过以下五个方面帮助大家比较全面地了解CUDA编程最重要的知识点,做到快速入门: GPU架构特点 CUDA线程模型

Python3快速入门(九)——Python3并发编程

Python3快速入门(九)--Python3并发编程 一.Python线程模块 1.线程简介 一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成.线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程本身不拥有系统资源,与进程内的其它线程共享进程的所有资源.一个进程中至少有一个线程,并作为程序的入口,即主线程,其它线程称为工作线程.???? 多线程,是指从软件或者硬件上实现多个线程并发执行的技术.支持多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,进而提升

Python3快速入门(十)——Python3网络编程

Python3快速入门(十)--Python3网络编程 一.socket模块简介 Python 提供了两个级别访问的网络服务,低级别的网络服务支持基本的 Socket,提供了标准的BSD Sockets API,可以访问底层操作系统Socket接口的全部方法:高级别的网络服务模块 SocketServer, 提供了服务器中心类,可以简化网络服务器的开发.socket不支持多并发,socketserver是对socket的再封装,简化网络服务器版的开发. 二.socket模块接口 1.socket