本文详细介绍了在Java中如何高效地从输入字符串中提取指定长度的单词。通过利用String.split()方法将句子分解为单词数组,并结合Java 8 Stream API的Filter()和toArray()操作,可以实现一个简洁、可读且功能强大的解决方案,从而轻松筛选出符合特定长度要求的单词。
任务概述
在编程实践中,我们经常需要处理文本数据。其中一个常见任务是从一个包含多个单词的句子中,根据指定的单词长度,筛选并返回所有符合条件的单词。例如,给定字符串“monday is a new day”和长度3,我们期望得到{“new”, “day”}。
传统的实现方式可能涉及手动遍历字符串,判断字符是否为空格来识别单词边界,然后截取子字符串并检查其长度。这种方法通常代码量较大,逻辑复杂,且容易出错,尤其是在处理多个连续空格或字符串开头/结尾的空格时。
推荐解决方案:结合split()与Stream API
Java 8引入的Stream API为处理集合数据提供了强大而简洁的工具。结合String.split()方法,我们可以非常优雅地解决这个问题。
核心思路
- 分解字符串: 使用String.split()方法将输入的句子按空格分割成一个单词数组。
- 创建流: 将单词数组转换为一个流(Stream)。
- 过滤单词: 使用流的filter()操作,根据每个单词的长度是否等于目标长度进行筛选。
- 收集结果: 使用toArray()操作将过滤后的单词收集到一个新的字符串数组中。
示例代码
以下是实现上述逻辑的Java代码:
import java.util.Arrays; import java.util.Objects; // 用于Objects.requireNonNULLElseGet,处理null或空字符串 public class wordExtractor { /** * 从给定字符串中提取所有指定长度的单词。 * * @param sentence 输入的句子字符串。 * @param wordLength 目标单词的长度。 * @return 包含所有符合长度要求的单词的字符串数组。 * 如果输入句子为空或null,则返回空数组。 */ public String[] findWordsByLength(String sentence, int wordLength) { // 1. 处理null或空字符串输入,避免NullPointerException // Objects.requireNonNullElseGet(sentence, () -> "") ensures sentence is not null // .trim() removes leading/trailing spaces // .split("s+") splits by one or more whitespace characters String[] words = Objects.requireNonNullElseGet(sentence, () -> "") .trim() .split("s+"); // 2. 将单词数组转换为流,并进行过滤和收集 return Arrays.stream(words) .filter(word -> !word.isEmpty() && word.length() == wordLength) // 过滤空字符串和符合长度的单词 .toArray(String[]::new); // 将结果收集到新的字符串数组中 } public static void main(String[] args) { WordExtractor extractor = new WordExtractor(); // 示例1 String s1 = "Monday is a new day"; int n1 = 3; // 3字母单词 String[] result1 = extractor.findWordsByLength(s1, n1); System.out.println("Input: "" + s1 + "", Length: " + n1 + " -> Result: " + Arrays.toString(result1)); // 预期: {"new", "day"} // 示例2 String s2 = "Monday is a new day"; int n2 = 2; // 2字母单词 String[] result2 = extractor.findWordsByLength(s2, n2); System.out.println("Input: "" + s2 + "", Length: " + n2 + " -> Result: " + Arrays.toString(result2)); // 预期: {"is"} // 示例3:包含多个空格 String s3 = " hello world java "; int n3 = 5; String[] result3 = extractor.findWordsByLength(s3, n3); System.out.println("Input: "" + s3 + "", Length: " + n3 + " -> Result: " + Arrays.toString(result3)); // 预期: {"hello", "world"} // 示例4:空字符串或null输入 String s4 = ""; int n4 = 3; String[] result4 = extractor.findWordsByLength(s4, n4); System.out.println("Input: "" + s4 + "", Length: " + n4 + " -> Result: " + Arrays.toString(result4)); // 预期: {} String s5 = null; int n5 = 3; String[] result5 = extractor.findWordsByLength(s5, n5); System.out.println("Input: "" + s5 + "", Length: " + n5 + " -> Result: " + Arrays.toString(result5)); // 预期: {} } }
代码解析
- Objects.requireNonNullElseGet(sentence, () -> “”): 这是一个健壮性处理,确保输入的sentence参数即使为null,也不会导致NullPointerException。如果sentence是null,它会替换为一个空字符串””。
- .trim(): 调用trim()方法去除字符串开头和结尾的空白字符。这有助于确保split()方法不会产生空字符串作为单词(例如,” hello”经过split(” “)可能得到{“”, “hello”})。
- .split(“s+”): 这是关键一步。
- Arrays.stream(words): 将上一步得到的words数组转换为一个Stream<String>。Stream API的所有操作都基于此流进行。
- .filter(word -> !word.isEmpty() && word.length() == wordLength): 这是流的中间操作,用于过滤元素。
- word -> !word.isEmpty():过滤掉可能由split()操作产生的空字符串。尽管trim()和split(“s+”)组合通常能避免这种情况,但多一层防御总是有益的。
- word.length() == wordLength:这是核心过滤条件,只保留长度与wordLength相等的单词。
- .toArray(String[]::new): 这是流的终止操作,将过滤后的流中的所有元素收集到一个新的String数组中。String[]::new是构造函数引用,用于指定创建数组的类型。
最佳实践与注意事项
- 描述性命名: 在代码中,使用具有描述性的方法名(如findWordsByLength而非howManyWord)和参数名(如wordLength而非n)至关重要。这大大提高了代码的可读性和可维护性,让其他开发者(或未来的你)能一眼理解代码的功能。
- 正则表达式的选用:
- split(” “):只按单个空格分割。如果字符串中有多个连续空格(如”hello world”),split(” “)会产生空字符串{“hello”, “”, “world”}。
- split(“s+”):按一个或多个空白字符分割。这是更推荐的做法,因为它能更鲁棒地处理各种空白字符(空格、制表符、换行符)以及连续的空白字符,避免产生空字符串。
- 处理空字符串或null输入: 在实际应用中,输入字符串可能为空或null。在调用split()之前,进行null检查和空字符串处理是良好的编程习惯,以避免运行时错误。Objects.requireNonNullElseGet是一个优雅的解决方案。
- 性能考量: 对于大多数常见的字符串长度和单词数量,Stream API的性能非常优秀。它在内部进行了优化,并且代码表达力强。对于极端性能敏感的场景(例如处理GB级别文本且需要微秒级响应),可能需要考虑更底层的字符遍历优化,但这种情况相对较少。
- 标点符号处理: 如果单词中可能包含标点符号(如”day.”),而你希望只匹配纯字母单词,你可能需要在split()之前或filter()之后额外添加一步处理,例如使用word.replaceAll(“[^a-zA-Z]”, “”)来去除标点符号。本教程的示例假定单词不含标点。
总结
通过结合String.split()方法和Java 8 Stream API,我们可以用非常简洁、高效且易于理解的方式,从字符串中提取指定长度的单词。这种现代java编程风格不仅提升了代码质量,也降低了维护成本。掌握这种模式对于处理文本数据和利用Java函数式编程特性至关重要。
立即学习“Java免费学习笔记(深入)”;
评论(已关闭)
评论已关闭