91、Java 中的迭代器、流与正则表达式

Java 中的迭代器、流与正则表达式

1. 迭代器与流

1.1 流与迭代器概述

虽然流并非数据存储对象,但仍可使用迭代器遍历其元素,这与使用迭代器遍历集合元素的方式类似。流 API 支持两种类型的迭代器:传统的 Iterator 和 JDK 8 新增的 Spliterator 。在处理并行流时, Spliterator 具有显著优势。

1.2 使用 Iterator 遍历流

可像使用迭代器遍历集合一样,使用迭代器遍历流。迭代器是实现了 java.util 包中 Iterator 接口的对象,其关键方法有 hasNext() next() 。若存在下一个待迭代元素, hasNext() 返回 true ,否则返回 false next() 方法返回迭代中的下一个元素。

JDK 8 还新增了处理基本类型流的迭代器,如 PrimitiveIterator PrimitiveIterator.OfDouble PrimitiveIterator.OfLong PrimitiveIterator.OfInt ,它们都继承自 Iterator 接口,工作方式与直接基于 Iterator 的迭代器类似。

要获取流的迭代器,可调用流的 iterator() 方法,示例如下:

Iterator<T> iterator()

其中, T 指定元素类型(基本类型流会返回相应基本类型的迭代器)。

以下是使用迭代器遍历流元素的示例代码:

// Use an iterator with a stream.
import java.util.*;
import java.util.stream.*;

class StreamDemo8 {
    public static void main(String[] args) {
        // Create a list of Strings.
        ArrayList<String> myList = new ArrayList<>();
        myList.add("Alpha");
        myList.add("Beta");
        myList.add("Gamma");
        myList.add("Delta");
        myList.add("Phi");
        myList.add("Omega");

        // Obtain a Stream to the array list.
        Stream<String> myStream = myList.stream();

        // Obtain an iterator to the stream.
        Iterator<String> itr = myStream.iterator();

        // Iterate the elements in the stream.
        while (itr.hasNext())
            System.out.println(itr.next());
    }
}

输出结果:

Alpha
Beta
Gamma
Delta
Phi
Omega

1.3 使用 Spliterator

Spliterator Iterator 提供了另一种选择,尤其在涉及并行处理时。一般来说, Spliterator Iterator 更复杂,它定义了多个方法,这里主要介绍三个关键方法:
- tryAdvance() :对下一个元素执行操作,然后推进迭代器。示例如下:

boolean tryAdvance(Consumer<? super T> action)

若存在下一个元素, tryAdvance() 返回 true ;若没有剩余元素,则返回 false 。由于 tryAdvance() 在没有更多元素需要处理时返回 false ,因此可简化迭代循环的构建,示例如下:

while(splitItr.tryAdvance( // perform action here ));

只要 tryAdvance() 返回 true ,就会对下一个元素执行操作;当返回 false 时,迭代结束。可见, tryAdvance() Iterator 中的 hasNext() next() 方法的功能合并为一个方法,提高了迭代效率。

以下是使用 Spliterator 替代 Iterator 的示例代码:

// Use a Spliterator.
import java.util.*;
import java.util.stream.*;

class StreamDemo9 {
    public static void main(String[] args) {
        // Create a list of Strings.
        ArrayList<String> myList = new ArrayList<>();
        myList.add("Alpha");
        myList.add("Beta");
        myList.add("Gamma");
        myList.add("Delta");
        myList.add("Phi");
        myList.add("Omega");

        // Obtain a Stream to the array list.
        Stream<String> myStream = myList.stream();

        // Obtain a Spliterator.
        Spliterator<String> splitItr = myStream.spliterator();

        // Iterate the elements of the stream.
        while (splitItr.tryAdvance((n) -> System.out.println(n)));
    }
}

输出结果与上述使用 Iterator 的示例相同。

  • forEachRemaining() :对每个未处理的元素执行指定操作,然后返回。示例如下:
default void forEachRemaining(Consumer<? super T> action)

此方法无需编写循环逐个遍历元素,这是 Spliterator 的另一个优势。

  • trySplit() :将正在迭代的元素分成两部分,返回一个指向其中一个分区的新 Spliterator ,另一个分区仍可通过原始的 Spliterator 访问。示例如下:
Spliterator<T> trySplit()

若无法拆分调用的 Spliterator ,则返回 null ;否则,返回指向分区的引用。以下是演示 trySplit() 方法的示例代码:

// Demonstrate trySplit().
import java.util.*;
import java.util.stream.*;

class StreamDemo10 {
    public static void main(String[] args) {
        // Create a list of Strings.
        ArrayList<String> myList = new ArrayList<>();
        myList.add("Alpha");
        myList.add("Beta");
        myList.add("Gamma");
        myList.add("Delta");
        myList.add("Phi");
        myList.add("Omega");

        // Obtain a Stream to the array list.
        Stream<String> myStream = myList.stream();

        // Obtain a Spliterator.
        Spliterator<String> splitItr = myStream.spliterator();

        // Now, split the first iterator.
        Spliterator<String> splitItr2 = splitItr.trySplit();

        // If splitItr could be split, use splitItr2 first.
        if (splitItr2 != null) {
            System.out.println("Output from splitItr2: ");
            splitItr2.forEachRemaining((n) -> System.out.println(n));
        }

        // Now, use the splitItr.
        System.out.println("\nOutput from splitItr: ");
        splitItr.forEachRemaining((n) -> System.out.println(n));
    }
}

输出结果:

Output from splitItr2:
Alpha
Beta
Gamma

Output from splitItr:
Delta
Phi
Omega

虽然在这个简单示例中拆分 Spliterator 并无实际价值,但在处理大型数据集的并行处理时,拆分操作具有重要意义。不过,在许多情况下,结合并行流使用其他流方法比手动使用 Spliterator 处理这些细节更为合适。 Spliterator 主要用于预定义方法都不适用的情况。

1.4 流 API 的其他探索

流 API 还有更多功能,以下是一些有用的方法:
| 方法 | 描述 |
| ---- | ---- |
| allMatch() anyMatch() noneMatch() | 用于判断流中是否有一个或多个元素满足指定的谓词 |
| count() | 获取流中元素的数量 |
| distinct() | 获取只包含唯一元素的流 |
| of() | 创建包含指定元素集的流 |

流 API 是 Java 的强大补充,随着时间推移,其功能可能会不断增强,建议定期查阅其 API 文档。

2. 核心 Java API 包

2.1 核心 API 包概述

Java 最初发布时包含了一组八个包,即核心 API,后续版本不断对其进行扩展。如今,Java API 包含大量包,其中一些包支持的专业领域超出了本文讨论范围,但有四个包值得关注: java.util.regex java.lang.reflect java.rmi java.text ,它们分别支持正则表达式处理、反射、远程方法调用(RMI)和文本格式化。此外,还会介绍 java.time 及其子包中的新日期和时间 API。

2.2 核心 Java API 包列表

以下是核心 Java API 包及其主要功能的列表:
| 包名 | 主要功能 |
| ---- | ---- |
| java.applet | 支持小程序的构建 |
| java.awt | 提供图形用户界面的功能 |
| java.awt.color | 支持颜色空间和配置文件 |
| java.awt.datatransfer | 实现与系统剪贴板之间的数据传输 |
| java.awt.dnd | 支持拖放操作 |
| java.awt.event | 处理事件 |
| java.awt.font | 表示各种字体 |
| java.awt.geom | 允许处理几何形状 |
| java.awt.im | 允许向文本编辑组件输入日语、中文和韩语字符 |
| java.awt.im.spi | 支持替代输入设备 |
| java.awt.image | 处理图像 |
| java.awt.image.renderable | 支持与渲染无关的图像 |
| java.awt.print | 支持通用打印功能 |
| java.beans | 允许构建软件组件 |
| java.beans.beancontext | 为 JavaBeans 提供执行环境 |
| java.io | 实现数据的输入和输出 |
| java.lang | 提供核心功能 |
| java.lang.annotation | 支持注解(元数据) |
| java.lang.instrument | 支持程序插桩 |
| java.lang.invoke | 支持动态语言 |
| java.lang.management | 支持执行环境的管理 |
| java.lang.ref | 允许与垃圾回收器进行交互 |
| java.lang.reflect | 在运行时分析代码 |
| java.math | 处理大整数和十进制数 |
| java.net | 支持网络编程 |
| java.nio | NIO 类的顶级包,封装缓冲区 |
| java.nio.channels | 封装 NIO 系统使用的通道 |
| java.nio.channels.spi | 支持通道的服务提供者 |
| java.nio.charset | 封装字符集 |
| java.nio.charset.spi | 支持字符集的服务提供者 |
| java.nio.file | 为文件提供 NIO 支持 |
| java.nio.file.attribute | 支持 NIO 文件属性 |
| java.nio.file.spi | 支持文件的 NIO 服务提供者 |
| java.rmi | 提供远程方法调用 |
| java.rmi.activation | 激活持久对象 |
| java.rmi.dgc | 管理分布式垃圾回收 |
| java.rmi.registry | 将名称映射到远程对象引用 |
| java.rmi.server | 支持远程方法调用 |
| java.security | 处理证书、密钥、摘要、签名和其他安全功能 |
| java.security.acl | 管理访问控制列表 |
| java.security.cert | 解析和管理证书 |
| java.security.interfaces | 定义 DSA(数字签名算法)密钥的接口 |
| java.security.spec | 指定密钥和算法参数 |
| java.sql | 与 SQL(结构化查询语言)数据库进行通信 |
| java.text | 格式化、搜索和操作文本 |
| java.text.spi | 支持 java.text 中文本格式化类的服务提供者 |
| java.time | 新日期和时间 API 的主要支持(JDK 8 新增) |
| java.time.chrono | 支持非公历的替代日历(JDK 8 新增) |
| java.time.format | 支持日期和时间格式化(JDK 8 新增) |
| java.time.temporal | 支持扩展的日期和时间功能(JDK 8 新增) |
| java.time.zone | 支持时区(JDK 8 新增) |
| java.util | 包含常用工具类 |
| java.util.concurrent | 支持并发工具 |
| java.util.concurrent.atomic | 支持在不使用锁的情况下对变量进行原子(即不可分割)操作 |
| java.util.concurrent.locks | 支持同步锁 |
| java.util.function | 提供多个函数式接口(JDK 8 新增) |
| java.util.jar | 创建和读取 JAR 文件 |
| java.util.logging | 支持记录与程序执行相关的信息 |
| java.util.prefs | 封装与用户偏好相关的信息 |
| java.util.regex | 支持正则表达式处理 |
| java.util.spi | 支持 java.util 中实用类的服务提供者 |
| java.util.stream | 支持新的流 API(JDK 8 新增) |
| java.util.zip | 读取和写入压缩与未压缩的 ZIP 文件 |

3. 正则表达式处理

3.1 正则表达式概述

java.util.regex 包支持正则表达式处理。正则表达式是描述字符序列的字符串模式,可用于在其他字符序列中查找匹配项。它可以指定通配符、字符集和各种量词,从而表示能匹配多个不同特定字符序列的通用形式。

3.2 支持正则表达式处理的类

3.2.1 Pattern 类

Pattern 类没有构造函数,需通过调用 compile() 工厂方法创建模式。示例如下:

static Pattern compile(String pattern)

其中, pattern 是要使用的正则表达式。 compile() 方法将字符串转换为可用于匹配的模式,并返回包含该模式的 Pattern 对象。创建 Pattern 对象后,可通过调用 matcher() 方法创建 Matcher 对象:

Matcher matcher(CharSequence str)

str 是要匹配的字符序列,即输入序列。 CharSequence 是一个定义只读字符集的接口, String 类实现了该接口,因此可以将字符串传递给 matcher() 方法。

3.2.2 Matcher 类

Matcher 类也没有构造函数,需通过调用 Pattern 类的 matcher() 方法创建。创建 Matcher 对象后,可使用其方法进行各种模式匹配操作。
- matches() :用于判断整个字符序列是否与模式匹配,示例如下:

boolean matches()

若序列与模式匹配,则返回 true ;否则返回 false 。需注意,整个序列必须与模式完全匹配,而不仅仅是子序列。
- find() :用于判断输入序列的子序列是否与模式匹配,示例如下:

boolean find()

若存在匹配的子序列,则返回 true ;否则返回 false 。该方法可多次调用,以查找所有匹配的子序列,每次调用从上次结束的位置开始。
- group() :用于获取包含最后匹配序列的字符串,示例如下:

String group()

若存在匹配,则返回匹配的字符串;若不存在匹配,则抛出 IllegalStateException 异常。
- start() end() :分别用于获取当前匹配在输入序列中的起始索引和结束索引的下一个位置,示例如下:

int start()
int end()

若不存在匹配,则这两个方法都会抛出 IllegalStateException 异常。
- replaceAll() :用于将所有匹配的序列替换为另一个序列,示例如下:

String replaceAll(String newStr)

newStr 是用于替换匹配模式的新字符序列,方法返回更新后的输入序列字符串。

3.3 正则表达式语法

正则表达式通常由普通字符、字符类、通配符和量词组成:
- 普通字符 :按原样匹配,例如模式 “xy” 只匹配输入序列 “xy”。换行符和制表符等特殊字符使用标准转义序列(以 \ 开头)指定,如 \n 表示换行符。普通字符也称为字面量。
- 字符类 :通过将字符放在方括号中定义,例如 [wxyz] 匹配 w x y z 。在方括号内使用 ^ 表示取反,如 [^wxyz] 匹配除 w x y z 之外的任何字符。还可以使用连字符指定字符范围,如 [1 - 9] 匹配 1 到 9 的数字。
- 通配符 . (点)是通配符,可匹配任何字符。例如,模式 “.” 可以匹配 “A”、”a”、”x” 等。
- 量词 :用于确定表达式的匹配次数,常见的量词如下:
- + :匹配一次或多次。
- * :匹配零次或多次。
- ? :匹配零次或一次。

例如,模式 “x+” 可以匹配 “x”、”xx”、”xxx” 等。若指定的表达式无效,通常会抛出 PatternSyntaxException 异常。

3.4 模式匹配示例

3.4.1 字面模式匹配
// A simple pattern matching demo.
import java.util.regex.*;

class RegExpr {
    public static void main(String args[]) {
        Pattern pat;
        Matcher mat;
        boolean found;

        pat = Pattern.compile("Java");
        mat = pat.matcher("Java");
        found = mat.matches(); // check for a match

        System.out.println("Testing Java against Java.");
        if (found) System.out.println("Matches");
        else System.out.println("No Match");

        System.out.println();

        System.out.println("Testing Java against Java 8.");
        mat = pat.matcher("Java 8"); // create a new matcher

        found = mat.matches(); // check for a match

        if (found) System.out.println("Matches");
        else System.out.println("No Match");
    }
}

输出结果:

Testing Java against Java.
Matches

Testing Java against Java 8.
No Match

此示例中,先创建包含 “Java” 的模式,然后分别使用 “Java” 和 “Java 8” 作为输入序列进行匹配。 matches() 方法要求整个序列与模式完全匹配。

3.4.2 使用 find() 方法查找子序列
// Use find() to find a subsequence.
import java.util.regex.*;

class RegExpr2 {
    public static void main(String args[]) {
        Pattern pat = Pattern.compile("Java");
        Matcher mat = pat.matcher("Java 8");

        System.out.println("Looking for Java in Java 8.");
        if (mat.find()) System.out.println("subsequence found");
        else System.out.println("No Match");
    }
}

输出结果:

Looking for Java in Java 8.
subsequence found

find() 方法用于查找输入序列中是否存在与模式匹配的子序列。

3.4.3 查找多个匹配的子序列
// Use find() to find multiple subsequences.
import java.util.regex.*;

class RegExpr3 {
    public static void main(String args[]) {
        Pattern pat = Pattern.compile("test");
        Matcher mat = pat.matcher("test 1 2 3 test");

        while (mat.find()) {
            System.out.println("test found at index " + mat.start());
        }
    }
}

输出结果:

test found at index 0
test found at index 11

通过多次调用 find() 方法,可以查找输入序列中所有匹配的子序列,并使用 start() 方法获取每个匹配的起始索引。

3.5 使用通配符和量词

3.5.1 使用量词
// Use a quantifier.
import java.util.regex.*;

class RegExpr4 {
    public static void main(String args[]) {
        Pattern pat = Pattern.compile("W+");
        Matcher mat = pat.matcher("W WW WWW");

        while (mat.find())
            System.out.println("Match: " + mat.group());
    }
}

输出结果:

Match: W
Match: WW
Match: WWW

模式 “W+” 可以匹配任意长度的连续 “W” 序列。

3.5.2 使用通配符和量词
// Use wildcard and quantifier.
import java.util.regex.*;

class RegExpr5 {
    public static void main(String args[]) {
        Pattern pat = Pattern.compile("e.+d");
        Matcher mat = pat.matcher("extend cup end table");

        while (mat.find())
            System.out.println("Match: " + mat.group());
    }
}

输出结果:

Match: extend cup end

默认情况下, find() 方法会匹配符合模式的最长序列。若要匹配最短序列,可在模式中添加 ? 量词,示例如下:

// Use the ? quantifier.
import java.util.regex.*;

class RegExpr6 {
    public static void main(String args[]) {
        // Use reluctant matching behavior.
        Pattern pat = Pattern.compile("e.+?d");
        Matcher mat = pat.matcher("extend cup end table");

        while (mat.find())
            System.out.println("Match: " + mat.group());
    }
}

输出结果:

Match: extend
Match: end

3.6 使用字符类

// Use a character class.
import java.util.regex.*;

class RegExpr7 {
    public static void main(String args[]) {
        // Match lowercase words.
        Pattern pat = Pattern.compile("[a-z]+");
        Matcher mat = pat.matcher("this is a test.");

        while (mat.find())
            System.out.println("Match: " + mat.group());
    }
}

输出结果:

Match: this
Match: is
Match: a
Match: test

通过使用字符类 [a - z] ,可以匹配由小写字母组成的单词。

3.7 使用 replaceAll() 方法

// Use replaceAll().
import java.util.regex.*;

class RegExpr8 {
    public static void main(String args[]) {
        String str = "Jon Jonathan Frank Ken Todd";

        Pattern pat = Pattern.compile("Jon.*? ");
        Matcher mat = pat.matcher(str);

        System.out.println("Original sequence: " + str);

        str = mat.replaceAll("Eric ");

        System.out.println("Modified sequence: " + str);
    }
}

输出结果:

Original sequence: Jon Jonathan Frank Ken Todd
Modified sequence: Eric Eric Frank Ken Todd

正则表达式 “Jon.*? ” 匹配以 “Jon” 开头,后跟零个或多个字符,以空格结尾的字符串,可将 “Jon” 和 “Jonathan” 替换为 “Eric”。

3.8 使用 split() 方法

// Use split().
import java.util.regex.*;

class RegExpr9 {
    public static void main(String args[]) {
        // Match lowercase words.
        Pattern pat = Pattern.compile("[ ,.!]");

        String strs[] = pat.split("one two,alpha9 12!done.");

        for (int i = 0; i < strs.length; i++)
            System.out.println("Next token: " + strs[i]);
    }
}

输出结果:

Next token: one
Next token: two
Next token: alpha9

split() 方法根据模式指定的分隔符将输入序列拆分为多个标记。

3.9 正则表达式操作流程

graph TD
    A[定义正则表达式模式] --> B[创建 Pattern 对象]
    B --> C[创建 Matcher 对象]
    C --> D{选择操作}
    D -->|matches()| E{整个序列是否匹配?}
    E -->|是| F[返回 true]
    E -->|否| G[返回 false]
    D -->|find()| H{是否有匹配子序列?}
    H -->|是| I[返回 true,可多次调用查找所有匹配]
    H -->|否| J[返回 false]
    D -->|replaceAll()| K[替换所有匹配序列]
    D -->|split()| L[拆分输入序列为标记]

正则表达式在 Java 中是一种强大的工具,可用于字符串的匹配、查找、替换和拆分等操作。通过合理使用正则表达式的语法和相关类的方法,可以高效地处理各种文本处理任务。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值