
在日常编程中,我们有时会遇到需要根据字符串中内嵌的数字来重新排列单词的需求。例如,给定字符串 "my1kiran4name2is3",其中每个单词后面都跟着一个数字(1-9),表示该单词在最终序列中的位置。我们的目标是根据这些数字,将单词重新排列为 "my name is kiran"。
示例:
按数字顺序排列后,结果应为:my (1) name (2) is (3) kiran (4)。
解决此问题的一种高效方法是结合使用 Java 的正则表达式(特别是零宽断言)和 Stream API。这种方法能够优雅地将单词与数字分离,并进行后续处理。
立即学习“Java免费学习笔记(深入)”;
我们将以字符串 "my1kiran4name2is3" 为例,逐步解释代码逻辑。
1. 初始分割:利用正向后行断言 ((?<=\d))
第一步是将原始字符串根据数字进行分割,但要保留数字作为每个片段的结尾。这里使用正则表达式 (?<=\d),它是一个正向后行断言(Positive Lookbehind)。这意味着它会匹配紧跟在一个数字 (\d) 后面的位置,但不会将数字本身包含在匹配结果中。
String string = "my1kiran4name2is3";
// string.split("(?<=\d)") 的结果是:["my1", "kiran4", "name2", "is3"]经过这一步,我们得到了一个包含“单词+数字”形式的字符串列表。
2. 二次分割与映射:利用正向先行断言 ((?=\d)) 并构建 Map
接下来,对上一步得到的每个片段(如 "my1"),我们需要将其进一步分割成单词和数字。这里使用 s.split("(?=\d)"),(?=\d) 是一个正向先行断言(Positive Lookahead)。它会匹配紧跟在一个数字 (\d) 前面的位置,但同样不将数字本身包含在匹配结果中。这使得数字成为分割后的第二个元素。
// 以 "my1" 为例,s.split("(?=\d)") 的结果是:["my", "1"]
// 以 "kiran4" 为例,s.split("(?=\d)") 的结果是:["kiran", "4"]然后,我们使用 Stream API 的 collect(Collectors.toMap(...)) 方法将这些分割后的数组转换为一个 Map<Integer, String>。数字字符串被解析为 Integer 作为键,单词作为值。
Map<Integer, String> map = Arrays.asList(string.split("(?<=\d)")) // 将分割结果转换为 List
.stream() // 创建 Stream
.map(s -> s.split("(?=\d)")) // 对每个元素进行二次分割
.collect(Collectors.toMap(e -> Integer.parseInt(e[1]), e -> e[0]));
// 最终得到的 map 可能是:{1="my", 4="kiran", 2="name", 3="is"}
// 注意:HashMap 的迭代顺序不确定,下文会详细说明。3. 重组字符串:连接 Map 中的值
最后一步是从 map 中提取单词并用空格连接。原始解决方案直接使用了 map.values().stream().collect(Collectors.joining(" "))。
string = map.values().stream()
.collect((Collectors.joining(" ")));
// 假设 map.values() 按键的顺序迭代,则结果为 "my name is kiran"import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.TreeMap; // 导入 TreeMap 以确保排序
public class StringSorter {
public static void main(String[] args) {
String inputString = "my1kiran4name2is3";
System.out.println("原始字符串: " + inputString);
// 步骤1 & 2: 分割并构建 Map
Map<Integer, String> wordMap =
Arrays.asList(inputString
.split("(?<=\d)")) // 第一次分割:["my1", "kiran4", "name2", "is3"]
.stream()
.map(s -> s.split("(?=\d)")) // 第二次分割:[["my", "1"], ["kiran", "4"], ...]
// 使用 TreeMap::new 确保 Map 内部按键排序
.collect(Collectors.toMap(
e -> Integer.parseInt(e[1]), // 键:数字
e -> e[0], // 值:单词
(oldValue, newValue) -> oldValue, // 处理重复键(此处不应发生)
TreeMap::new // 指定使用 TreeMap
));
// 步骤3: 从 Map 中按键顺序提取值并连接
String sortedString = wordMap
.values() // TreeMap 的 values() 方法会按键的自然顺序迭代
.stream()
.collect(Collectors.joining(" "));
System.out.println("排序后的字符串: " + sortedString);
}
}输出:
原始字符串: my1kiran4name2is3 排序后的字符串: my name is kiran
代码可读性与维护性: 虽然上述解决方案利用了正则表达式和 Stream API 的强大功能,但其简洁性有时会牺牲一定的可读性,特别是对于不熟悉正则表达式零宽断言的开发者。在实际项目中,如果团队成员对这些高级特性不熟悉,可能需要添加详细注释或考虑更直观(尽管可能更冗长)的实现方式。
Map 的排序问题: 原始的 Collectors.toMap 默认创建的是 HashMap。HashMap 不保证元素的迭代顺序,这意味着 map.values().stream() 所产生的单词顺序可能不是按数字从小到大排列的。为了确保最终输出的单词严格按照数字顺序排列,应采取以下两种策略之一:
数字范围与格式: 本解决方案假设数字为单个数字(1-9)。如果字符串中的数字可能是多位数(例如 "word10"),则正则表达式 \d 需要修改为 \d+(匹配一个或多个数字),并且相应的 split 逻辑需要确保正确处理多位数字。例如,"word10" 使用 s.split("(?=\d)") 仍会得到 ["word", "10"],所以对于多位数是兼容的。但如果数字前或后有其他字符,则需要更复杂的正则匹配。
本教程详细展示了如何利用 Java 的正则表达式(特别是正向先行和后行断言)和 Stream API 来解决根据内嵌数字对字符串中单词进行排序的问题。通过将复杂的字符串处理分解为逻辑清晰的步骤:分割、映射和重组,并注意 Map 的排序特性,我们可以构建出既高效又准确的解决方案。在实际应用中,除了关注功能的实现,代码的可读性和健壮性(如处理不同数字范围和确保排序)同样至关重要。
以上就是使用 Java 正则表达式和 Stream API 对字符串中的单词进行数字排序的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号