初识Java 12-3 流

news2024/12/25 9:17:38

目录

终结操作

将流转换为一个数组(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 { // 一个基本的数据对象Pair
    public 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;
    }

    @Override
    public 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); // 组合所有的ArrayList

        words.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;
    }

    @Override
    public 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.coloradmin.cn/o/1061702.html

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

相关文章

Java 8新特性:DateTime、Lambda、Stream的强大功能解析

&#x1f60a; 作者&#xff1a; 一恍过去 &#x1f496; 主页&#xff1a; https://blog.csdn.net/zhuocailing3390 &#x1f38a; 社区&#xff1a; Java技术栈交流 &#x1f389; 主题&#xff1a; Java 8新特性&#xff1a;DateTime、Lambda、Stream的强大功能解析 ⏱️…

案例题--Web应用考点

案例题--Web应用考点 负载均衡技术微服务XML和JSON无状态和有状态真题 在选择题中没有考察过web的相关知识&#xff0c;主要就是在案例分析题中考察 负载均衡技术 应用层负载均衡技术 传输层负载均衡技术 就近的找到距离最近的服务器&#xff0c;并进行分发 使用户就近获取…

超市便利店批发零售小程序商城的作用是什么

超市便利店甚至是商场&#xff0c;所售卖的产品广而多&#xff0c;其线上线下商家数量也非常庞大&#xff0c;对传统超市便利店来说&#xff0c;往往会优先发力线下经营&#xff0c;然而随着互联网电商冲击&#xff0c;传统线下经营也面临&#xff1a;拓客引流难、产品销售难、…

数组篇 第一题:删除排序数组中的重复项

更多精彩内容请关注微信公众号&#xff1a;听潮庭。 第一题&#xff1a;删除排序数组中的重复项 给你一个 非严格递增排列 的数组 nums &#xff0c;请你 原地 删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应…

算法:最近公共祖先(LCA)

有根树中&#xff0c;每一个点都有好几个祖先&#xff08;在往根节点走的过程中遇到的都是它的祖先&#xff09;&#xff0c;一般化&#xff0c;把本身也称为它的祖先 对于两个点&#xff0c;离它们最近的一个公共祖先被称为最近公共祖先 法一&#xff1a;向上标记法&#xf…

6轮面试阿里Android开发offer,薪资却从21k降到17k,在逗我?

一小伙工作快3年了&#xff0c;拿到了阿里云Android开发岗位P6的offer&#xff0c;算HR面一起&#xff0c;加起来有6轮面试了&#xff0c;将近3个月的时间&#xff0c;1轮同级 1轮Android用人部门leader 1轮Android 组leader 1轮项目CTO 1轮HR 1轮HRBP。 一路上各种事件分…

图示矩阵分解

特征值与特征向量 设 A A A 是 n 阶矩阵&#xff0c;如果存在数 λ \lambda λ 和 n 维非零列向量 x x x&#xff0c;满足关系式&#xff1a; A x λ x ( 1 ) Ax \lambda x\quad\quad(1) Axλx(1) 则数 λ \lambda λ 称为矩阵 A A A 的特征值&#xff0c;非零向量 x…

基于阴阳对优化的BP神经网络(分类应用) - 附代码

基于阴阳对优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于阴阳对优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.阴阳对优化BP神经网络3.1 BP神经网络参数设置3.2 阴阳对算法应用 4.测试结果&#x…

学习记忆——宫殿篇——记忆宫殿——数字编码——扑克牌记忆

扑克牌我们可以通过以下3点进行识记&#xff1a; 1、先把扑克牌进行编码转换 2、确定要进行记忆的记忆宫殿 3、把扑克牌与记忆宫殿一一对应 首先54张扑克牌除去大小王后剩下52张&#xff0c;因为世界赛不需要记忆大小王。52张扑克牌都有对应的编码&#xff0c;每2张扑克牌对应…

阿里云新账户、老账号、产品首购和同人账号什么意思?

阿里云账号分为云新账户、老账户、同人账号和同一用户有什么区别&#xff1f;阿里云官方推出的活动很多是限制账号类型的&#xff0c;常见的如阿里云新用户&#xff0c;什么是阿里云新用户&#xff1f;是指从未在阿里云官网购买过云产品的账号。下面阿小云来详细说下什么是阿里…

docker系列6:docker安装redis

传送门 docker系列1&#xff1a;docker安装 docker系列2&#xff1a;阿里云镜像加速器 docker系列3&#xff1a;docker镜像基本命令 docker系列4&#xff1a;docker容器基本命令 docker系列5&#xff1a;docker安装nginx Docker安装redis 通过前面4节&#xff0c;对docke…

JS中创建对象有几种方法

除了使用Object构造函数或者字面量都可以创建对象&#xff0c;但是也有缺点就是使用同一个接口创建很多对象&#xff0c;会产生大量的重复代码。 1. 工厂模式 简单来说就是把Object创建对象使用函数进行封装&#xff0c;然后再返回创建的对象&#xff0c;就可以创建多个相同对…

使用hugo+github搭建免费个人博客

使用hugogithub搭建免费个人博客 前提条件 win11电脑一台电脑安装了git电脑安装了hugogithub账号一个 个人博客本地搭建 初始化一个博客 打开cmd窗口&#xff0c;使用hugo新建一个博客工程 hugo new site blogtest下载主题 主题官网&#xff1a;themes.gohugo.io 在上面…

快手直播显示请求过快

快手直播显示请求过快 问题描述情况一问题描述原因分析解决方案:情况二问题描述解决方法问题描述 在使用快手直播网页版时,如果我们的请求过于频繁,系统可能无法及时显示所需内容。这种情况下,我们会收到一个稍后重试的提示。一般有两种情况。一种是直接返回一段json,里面…

园林园艺服务经营小程序商城的作用是什么

园林园艺属于高单价服务&#xff0c;同时还有各种衍生服务&#xff0c;对企业来说&#xff0c;多数情况下都是线下生意拓展及合作等&#xff0c;但其实线上也有一定深度&#xff0c;如服务售卖或园艺产品售卖等。 基于线上发展可以增强获客引流、品牌传播、产品销售经营、会员…

云原生微服务 第六章 Spring Cloud中使用OpenFeign

系列文章目录 第一章 Java线程池技术应用 第二章 CountDownLatch和Semaphone的应用 第三章 Spring Cloud 简介 第四章 Spring Cloud Netflix 之 Eureka 第五章 Spring Cloud Netflix 之 Ribbon 第六章 Spring Cloud 之 OpenFeign 文章目录 系列文章目录前言1、OpenFeign的实现…

mybatis-plus控制台打印sql(mybatis-Log)

配置了mybatis-plus.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl&#xff1b;但是mybatis执行的sql没有输出 需要检查点&#xff1a; 1、日志级别设置&#xff1a;请确保你的日志级别配置正确。如果日志级别设置得太低&#xff0c;可能导致SQL语句不…

软件工程与计算总结(四)项目管理基础

目录 一.项目和项目管理 二.团队组织与管理 三.软件质量保障 四.软件配置管理 五.项目实践 一.项目和项目管理 1.软件开发远不是纯粹的编程&#xff0c;随着软件规模的增长&#xff0c;软件开发活动也变得越来越复杂~ 2.软件项目就是要将所有的软件开发活动组织起来&#…

云原生Kubernetes:K8S集群kubectl命令汇总

目录 一、理论 1.概念 2. kubectl 帮助方法 3.kubectl 子命令使用分类 4.使用kubectl 命令的必要环境 5.kubectl 详细命令 一、理论 1.概念 kubectl是一个命令行工具&#xff0c;通过跟 K8S 集群的 API Server 通信&#xff0c;来执行集群的管理工作。 kubectl命令是操…

嵌入式Linux裸机开发(一)基础介绍及汇编LED驱动

系列文章目录 文章目录 系列文章目录前言IMX6ULL介绍主要资料IO表现形式 汇编LED驱动原理图初始化流程时钟设置IO复用设置电气属性设置使用GPIO 编写驱动编译程序编译.o文件地址链接.elf格式转换.bin反汇编&#xff08;其他&#xff09; 综合成Makefile完成一步编译烧录程序imx…