初识Java 12-3 流

目录

终结操作

将流转换为一个数组(toArray)

在每个流元素上应用某个终结操作(forEach)

收集操作(collect)

组合所有的流元素(reduce)

匹配(*Match)

选择一个元素(find*)

获得流相关的信息


本笔记参考自: 《On Java 中文版》


终结操作

||| 终结操作:这些操作会接受一个流,并生成一个最终结果。

        终结操作不会把任何东西发给某个后端的流。因此,终结操作总是我们在一个管线内可以做的最后一件事

将流转换为一个数组(toArray)

        可以通过toArray()操作将流转换为数组:

  • toArray():将流元素转换到适当的数组中。
  • toArray(generator)generator用于在特定情况下分配自己的数组存储。

        在流操作生成的内容必须以数组形式进行使用时,toArray()就很有用了:

【例子:将随机数存储在数组中,以流的形式复用】

import java.util.Arrays;
import java.util.Random;
import java.util.stream.IntStream;public class RandInts {private static int[] rints =new Random(47).ints(0, 1000).limit(100).toArray();public static IntStream rands() {return Arrays.stream(rints);}
}

        通过这种方式,我们可以保证每次得到的是相同的流。


在每个流元素上应用某个终结操作(forEach)

        有两个很常见的终结操作:

  1. forEach(Consumer)
  2. forEachOrdered(Consumer):这个版本可以确保forEach对元素的操作顺序是原始的流的顺序。

        通过使用parallel()操作,可以使forEach以任何顺序操作元素。

    parallel()的介绍:这一操作会让Java尝试在多个处理器上执行操作。parallel()可以将流进行分割,并在不同处理器上运行每个流。

        下面的例子通过引入parallel()来展示forEachOrdered(Consumer)的作用。

【例子:forEachOrdered(Consumer)的作用】

import static streams.RandInts.*;public class ForEach {static final int SZ = 14;public static void main(String[] args) {rands().limit(SZ).forEach(n -> System.out.format("%d ", n));System.out.println();rands().limit(SZ).parallel().forEach(n -> System.out.format("%d ", n));System.out.println();rands().limit(SZ).parallel().forEachOrdered(n -> System.out.format("%d ", n));System.out.println();}
}

        程序执行的结果是:

        在第一个流中,因为没有使用parallel(),所以结果显示的顺序就是它们从rands()中出现的顺序。而在第二个流中,parallel()的使用使得输出的顺序发生了变化。这就是因为有多个处理器在处理这个问题(若多执行几次,会发现输出的顺序会不一样,这就是多处理器处理带来的不确定性)

        在最后一个流中,尽管使用了parallel(),但forEachOrdered()使得结果回到了原始的顺序。

    所以,对非parallel()的流,使用forEachOrdered()不会有任何影响。


收集操作(collect)

  • collect(Collector):使用这个Collector将流元素累加到一个结果集合中。
  • collect(Supplier, BiConsumer, BiConsumer):和上一个collect()不同的地方在于:
    • Supplier会创建一个新的结果集合。
    • 第一个BiConsumer用来将下一个元素包含到结果集合中。
    • 第二个BiConsumer用来将两个值进行组合。

    collect()的第二个版本,会在每次被调用时,使用Supplier生成一个新的结果集合。这些集合就是通过第二个BiConsumer组合成一个最终结果的。

        可以将流元素收集到任何特定种类的集合中。例如:假设我们需要把条目最终放入到一个TreeSet中。尽管Collectors中没有特定的toTreeSet()方法,但我们可以使用Collectors.toCollection(),将任何类型的构造器引用传递给toCollection()

【例子:将提取的单词放入到TreeSet()中】

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;public class TreeSetOfWords {public static void main(String[] arg) throws Exception {Set<String> words2 =Files.lines(Paths.get("TreeSetOfWords.java")).flatMap(s -> Arrays.stream(s.split("\\W+"))) .filter(s -> !s.matches("\\d+")) // 删除数字.map(String::trim) // 删除可能存在的留白.filter(s -> s.length() > 2).limit(100).collect(Collectors.toCollection(TreeSet::new));System.out.println(words2);}
}

        程序执行的结果是:

        在这里,Arrays.stream(s.split("\\W+"))会将接收的文本行进行分割,获得的数组会变为Stream,然后其结果又通过flatMap()被展开成一个由单词组成的Stream

【例子:从流生成一个Map

import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;class Pair { // 一个基本的数据对象Pairpublic final Character c;public final Integer i;public Pair(Character c, Integer i) {this.c = c;this.i = i;}public Character getC() {return c;}public Integer getI() {return i;}@Overridepublic String toString() {return "Pair{" +"c=" + c +", i=" + i +'}';}
}class RandomPair {Random rand = new Random(47);// 这是一个无限大的迭代器,指向随机生成的大写字母Iterator<Character> capChars = rand.ints(65, 91) // 65~91,即大写字母对应的ASCII值.mapToObj(i -> (char) i).iterator();public Stream<Pair> stream() { // 生成一个Pair流return rand.ints(100, 1000).distinct() // 移除流中的重复元素.mapToObj(i -> new Pair(capChars.next(), i)); // 组合成Pair对象的流}
}public class MapCollector {public static void main(String[] args) {Map<Integer, Character> map =new RandomPair().stream().limit(8).collect(Collectors.toMap(Pair::getI, Pair::getC));System.out.println(map);}
}

        程序执行的结果是:

        在大多数情况下,java.util.stream.Collectors中都会存在我们所需的Collector。而如果找不到我们所需要的,这时候就可以使用collect()的第二种形式。

【例子:collect()的第二种形式】

import java.util.ArrayList;public class SpecialCollector {public static void main(String[] arg) throws Exception {ArrayList<String> words =FileToWords.stream("Cheese.dat").collect(ArrayList::new, // 创建新的结果集合ArrayList::add, // 将下一个元素加入集合中ArrayList::addAll); // 组合所有的ArrayListwords.stream().filter(s -> s.equals("cheese")).forEach(System.out::println);}
}

        程序执行的结果是:


组合所有的流元素(reduce)

  • reduce(BinaryOperator):使用BinaryOperator来组合所有的元素。若流为空,返回一个Optional。
  • reduce(identity, BinaryOperator):和上述一致,不同之处在于会将identity作为这个组合的初始值。因此若流为空,会得到一个identity作为结果。
  • reduce(identity, BiFuncton, BinaryOperator)BiFunction提供累加器,BinaryOperator提供组合函数。这种形式会更为高效。但若只论效果,可以通过组合显式的map()reduce()操作来替代。

    这些操作之所以使用reduce命名,是因为它们会获取一系列的输入元素,并将它们组合(简化)成一个单一的汇总结果。

【例子:reduce()操作的演示】

import java.util.Random;
import java.util.stream.Stream;class Frobnitz {int size;Frobnitz(int sz) {size = sz;}@Overridepublic String toString() {return "Frobnitz{" +"size=" + size +'}';}// 生成器static Random rand = new Random(47);static final int BOUND = 100;static Frobnitz supply() {return new Frobnitz(rand.nextInt(BOUND));}
}public class Reduce {public static void main(String[] args) {Stream.generate(Frobnitz::supply).limit(10).peek(System.out::println).reduce((fr0, fr1) -> fr0.size < 50 ? fr0 : fr1) // 若fr0的size小于50,接受fr0,否则接受fr1.ifPresent(System.out::println);}
}

        程序执行的结果是:

        Forbnitz有一个自己的生成器,叫做supply()。在上述例子中,我们把方法引用Frobnitz::supply传递给了Stream.generate,因为它和Supplier<Frobnitz>是签名兼容的(又称结构一致性)。

    Supplier是一个函数式接口。在这里Supplier<Frobnitz>相当于一个返回Frobnitz对象的无参函数。

        在使用reduce()时,我们没有使用identity作为初始值。这意味着reduce()会生成一个Optional,当结果不为empty时,才会执行Consumer<Frobnitz>(即lambda表达式)。再看lambda表达式本身:

(fr0, fr1) -> fr0.size < 50 ? fr0 : fr1

其中的第一个参数fr0是上次调用这个reduce()时带回的结果,第二个参数fr1是来自流中的新值。


匹配(*Match)

  • allMatch(Predicate):通过Predicate检测流中的元素,若每一个元素结果都为true,则返回true。若遇到false,则发生短路 —— 在遇到第一个false后,停止计算。
  • anyMatch(Predicate):同样使用Predicate进行检测,若任何一个元素得到true,则返回true。在遇到第一个true时,发生短路。
  • noneMatch(Predicate):进行检测,若没有元素得到true,则返回true。在遇到第一个true时,发生短路。

【例子:Match引发的短路行为】

import java.util.function.BiPredicate;
import java.util.function.Predicate;
import java.util.stream.IntStream;
import java.util.stream.Stream;interface Matcher extends // 能够匹配所有的Stream::*Match函数的模式BiPredicate<Stream<Integer>, Predicate<Integer>> {
}public class Matching {static void show(Matcher match, int val) {System.out.println(match.test(IntStream.rangeClosed(1, 9) // 返回一个从1~9递增的Integer序列.boxed().peek(n -> System.out.format("%d ", n)),n -> n < val));}public static void main(String[] args) {show(Stream::allMatch, 10);show(Stream::allMatch, 4);show(Stream::anyMatch, 2);show(Stream::anyMatch, 0);show(Stream::noneMatch, 5);show(Stream::noneMatch, 0);}
}

        程序执行的结果是:

        BiPredicate是一个二元谓词,所以他会接受两个参数,并返回truefalse。其中,第一个参数是我们要测试的数值的流,第二个参数是谓词Predicate本身。


选择一个元素(find*)

  • findFirst():返回一个Optional,其中包含了流中的第一个元素。若流中没有元素,则返回Optional.empty
  • findAny():返回一个Optional,其中包含了流中的某个元素。若流中没有元素,则返回Optional.empty

【例子:find*操作的使用】

import static streams.RandInts.*;public class SelectElement {public static void main(String[] args) {System.out.println(rands().findFirst().getAsInt());System.out.println(rands().parallel().findFirst().getAsInt());System.out.println(rands().findAny().getAsInt());System.out.println(rands().parallel().findAny().getAsInt());}
}

        程序执行的结果是:

        无论流是否并行,findFirst()总会选择流中的第一个元素。但findAny()有些不同,在非并行的流中,findAny()会选择第一个元素,而当这个流是并行流时,findAny()有可能选择第一个元素之外的元素。

        若需要某个流中的最后一个元素,可以使用reduce()

【例子:选择流中的最后一个元素】

import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.IntStream;
import java.util.stream.Stream;public class LastElement {public static void main(String[] args) {OptionalInt last = IntStream.range(10, 20).reduce((n1, n2) -> n2);System.out.println(last.orElse(-1));// 对于非数值对象Optional<String> lastobj =Stream.of("one", "two", "three").reduce((n1, n2) -> n2);System.out.println(lastobj.orElse("不存在任何元素"));}
}

        程序执行的结果是:

        reduce((n1, n2) -> n2)语句可以用两个元素这的后一个替换这两个元素,通过这种方式就可以获得流中的最后一个元素了。


获得流相关的信息

  • count():获得流中元素的数量。
  • max(Comparator):通过Comparator确定这个流中的“最大元素”。
  • min(Comparator):确定这个流中的“最小元素”。

【例子:使用String预设的Comparator获取信息】

public class Informational {public static void main(String[] args) throws Exception {System.out.println(FileToWords.stream("Cheese.dat").count());System.out.println(FileToWords.stream("Cheese.dat").min(String.CASE_INSENSITIVE_ORDER).orElse("NONE"));System.out.println(FileToWords.stream("Cheese.dat").max(String.CASE_INSENSITIVE_ORDER).orElse("NONE"));}
}

        程序执行的结果是:

        其中,max()min()都会返回Optional,这里出现的orElse()是用来获取其中的值的。

获得数值化流相关的信息

  • average():获得平均值。
  • max()min():因为处理的是数值化的流,所以不需要Comparator
  • sum():将流中的数值进行累加。
  • summaryStatistics():返回可能有用的摘要数据(但我们也可以使用直接方法获取这些数据)。

【例子:数值化流的信息获取(以IntStream为例)】

import static streams.RandInts.*;public class NumericStreamInfo {public static void main(String[] args) {System.out.println(rands().average().getAsDouble());System.out.println(rands().max().getAsInt());System.out.println(rands().min().getAsInt());System.out.println(rands().sum());System.out.println(rands().summaryStatistics());}
}

        程序执行的结果是:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/95380.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LLM下半场之Agent基础能力概述:Profile、Memory、Plan、Action、Eval学习笔记

一.Agent发展将会是LLM的下半场 目前大家都在讨论LLM&#xff0c;LLM解决的问题是帮助机器像人类一样理解彼此的意图&#xff0c;本质上来讲&#xff0c;LLM更像是一个技术或者工具。但是人类社会发生变革的引线&#xff0c;往往是一个产品或者解决方案&#xff0c;比如电池技…

Linux【网络】数据链路层

Linux【网络】数据链路层 数据链路层以太网帧格式对比理解MAC地址和IP地址ARP协议--地址解析协议ARP工作流程ARP请求ARP应答 其他协议DNS-域名解析协议ICMP--网络层协议NAT技术NAPT 正向代理与反向代理 数据链路层 数据链路层用于两个设备&#xff0c;同一数据链路节点之间的信…

栈和队列的实现

用栈实现队列 1.分析2.代码 1.分析 2.代码 class MyQueue {private Stack<Integer> s1;private Stack<Integer> s2;public MyQueue() {s1 new Stack<>();s2 new Stack<>();}public void push(int x) {s1.push(x);}public int pop() {if(empty()){re…

山西电力市场日前价格预测【2023-10-05】

日前价格预测 预测说明&#xff1a; 如上图所示&#xff0c;预测明日&#xff08;2023-10-05&#xff09;山西电力市场全天平均日前电价为363.87元/MWh。其中&#xff0c;最高日前电价为649.89元/MWh&#xff0c;预计出现在18: 45。最低日前电价为291.58元/MWh&#xff0c;预计…

微信公众号模板消息First,Remark字段不显示,备注字段不见了

今天在开发公众号过程中有个需求发模板消息我设置的如下 成绩单打印通知&#xff01;姓名&#xff1a;{{name.DATA}} 学号&#xff1a;{{stuid.DATA}}状态&#xff1a;{{status.DATA}}时间&#xff1a;{{date.DATA}} 备注&#xff1a;{{remark.DATA}} 然后发完通知发现《…

矩阵的c++实现(2)

上一次我们了解了矩阵的运算和如何使用矩阵解决斐波那契数列&#xff0c;这一次我们多看看例题&#xff0c;了解什么情况下用矩阵比较合适。 先看例题 1.洛谷P1939 【模板】矩阵加速&#xff08;数列&#xff09; 模板题应该很简单。 补&#xff1a;1<n<10^9 10^9肯定…

成都建筑模板批发市场在哪?

成都作为中国西南地区的重要城市&#xff0c;建筑业蓬勃发展&#xff0c;建筑模板作为建筑施工的重要材料之一&#xff0c;在成都也有着广泛的需求。如果您正在寻找成都的建筑模板批发市场&#xff0c;广西贵港市能强优品木业有限公司是一家值得关注的供应商。广西贵港市能强优…

mysql面试题16:说说分库与分表的设计?常用的分库分表中间件有哪些?分库分表可能遇到的问题有哪些?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:说说分库与分表的设计? 在MySQL中,分库与分表是常用的数据库水平扩展技术,可以提高数据库的吞吐量和扩展性。下面将具体讲解MySQL中分库与分表…

<C++> String

目录 一、标准库中的string类 1. string类 2. string类的常用接口说明 2.1 string类对象的常见构造 2.2 string类对象的容量操作 2.3 string类对象的访问及遍历操作 2.4 string类对象的修改操作 2.5 string类非成员函数 总结 前言 C语言中&#xff0c;字符串是以 \0 结尾的一些…

[软件工具]opencv-svm快速训练助手教程解决opencv C++ SVM模型训练与分类实现任务支持C# python调用

opencv中已经提供了svm算法可以对图像实现多分类&#xff0c;使用svm算法对图像分类的任务多用于场景简单且对时间有要求的场景&#xff0c;因为opencv的svm训练一般只需要很短时间就可以完成训练任务。但是目前网上没有一个工具很好解决训练问题&#xff0c;大部分需要自己编程…

AWS Lambda Golang HelloWorld 快速入门

操作步骤 以下测试基于 WSL2 Ubuntu 22.04 环境 # 下载最新 golang wget https://golang.google.cn/dl/go1.21.1.linux-amd64.tar.gz# 解压 tar -C ~/.local/ -xzf go1.21.1.linux-amd64.tar.gz# 配置环境变量 PATH echo export PATH$PATH:~/.local/go/bin >> ~/.bashrc …

密码技术 (6) - 证书

一. 前言 前面介绍的公钥密码和数字签名&#xff0c;都无法解决一个问题&#xff0c;那就是判断自己获取的公钥是否期望的&#xff0c;不能确定公钥是否被中间攻击人掉包。所以&#xff0c;证书的作用是用来证明公钥是否合法的。本文介绍的证书就是解决证书的可靠性的技术。 二…

Python3数据科学包系列(一):数据分析实战

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 认识下数据科学中数据处理基础包: (1)NumPy 俗话说: 要学会跑需先…

React框架核心原理

一、整体架构 三大核心库与对应的组件 history -> react-router -> react-router-dom react-router 可视为react-router-dom 的核心&#xff0c;里面封装了<Router>&#xff0c;<Route>&#xff0c;<Switch>等核心组件,实现了从路由的改变到组件的更新…

sheng的学习笔记-【中文】【吴恩达课后测验】Course 1 - 神经网络和深度学习 - 第三周测验

课程1_第3周_测验题 目录&#xff1a;目录 第一题 1.以下哪一项是正确的&#xff1f; A. 【  】 a [ 2 ] ( 12 ) a^{[2](12)} a[2](12)是第12层&#xff0c;第2个训练数据的激活向量。 B. 【  】X是一个矩阵&#xff0c;其中每个列都是一个训练示例。 C. 【  】 a 4 […

【一、灵犀考试系统项目设计、框架搭建】

一、创建数据库 1、打开power designer&#xff0c;新建数据库模型 2、新建数据表&#xff0c;以及关系 【注意】 图片的类型有两种&#xff1a;varbinary 和 image varbinary : 二进制字节流&#xff0c;可以自动控制长度 image : 最大可放2G图片 3、创建数据库&#…

国庆假期作业day2

作业&#xff1a;创建一个双向链表&#xff0c;将26个英文字母通过头插的方式插入到链表中&#xff0c;通过尾删的方式将数据读取出来并删除 #ifndef _TEXT_H #define _TEXT_H #include<myhead.h> typedef int datatype; typedef struct dblist {union {datatype data;/…

后端面经学习自测(二)

文章目录 1、Http1.1和2.0的区别大概是什么&#xff1f;HTTP & HTTPS 2、HTTP&#xff0c;用户后续的操作&#xff0c;服务端如何知道属于同一个用户cookie & session & token手机验证码登录流程SSO单点登录 3、如果服务端是一个集群机器&#xff1f;4、hashmap是线…

[React源码解析] React的设计理念和源码架构 (一)

任务分割异步执行让出执法权 文章目录 1.React的设计理念1.1 Fiber1.2 Scheduler1.3 Lane1.4 代数效应 2.React的源码架构2.1 大概图示2.2 jsx2.3 Fiber双缓存2.4 scheduler2.5 Lane模型2.6 reconciler2.7 renderer2.8 concurrent 3.React源码调试 1.React的设计理念 Fiber: 即…

项目进展(八)-编写代码,驱动ADS1285

一、代码 根据芯片的数据手册编写部分驱动&#xff0c;首先看部分引脚的波形&#xff1a; DRDY: CS&#xff1a; 首先在代码初始化时连续写入三个寄存器&#xff1a; void WriteReg(uint8_t startAddr, uint8_t *regData, uint8_t number) {uint8_t i0;// 循环写number1次…