精通 Java 中的 contains() 方法:如何高效执行子串搜索

目次

1. 引言:为什么字符串搜索在 Java 中很重要

字符串操作是 Java 编程中最常用的操作之一。
无论是检查用户输入、解析文件内容,还是搜索特定关键字,通常都需要判断某个单词是否出现在给定的字符串中。

为满足这些需求,Java 提供了一个便利的方法 contains()
使用该方法,你可以轻松判断一个字符串是否 部分包含 另一个字符串。
例如,如果想检查错误信息中是否包含特定关键字,contains() 只需一行代码即可实现。

在处理大量文本的场景——如 Web 应用、API 处理或日志分析——中,contains() 方法极大提升了代码的可读性和可维护性。
但也需要注意大小写敏感以及 null 的可能性等重要因素。

本文将详细阐述 Java 的 contains() 方法——从基本用法、常见错误到与其他方法的区别以及实际应用。
我们的目标是为初学者以及在实际项目中使用 Java 的开发者提供有价值的信息。

2. contains() 方法的基本语法与特性

Java 的 contains() 方法用于判断一个字符串是否 部分包含 另一个字符串。
其语法非常简洁,却在日常编程任务中极具实用性,使用频率很高。

基本语法

boolean result = targetString.contains(searchString);

该方法属于 String 类,接受一个 CharSequence(通常是 String)作为参数。
返回值为 boolean:如果目标字符串包含给定的子串则返回 true,否则返回 false

示例代码

String message = "Java programming is fun!";
boolean hasKeyword = message.contains("programming");

System.out.println(hasKeyword); // Output: true

在上面的示例中,子串 "programming" 出现在目标字符串中,因此 contains() 返回 true

方法特性

  • 仅检查部分匹配:如果需要完全匹配,请使用 equals()
  • 大小写敏感:例如,"Java""java" 被视为不同(后文会详细说明)。
  • 不支持正则表达式:因为它仅检查字符串是否存在,若需模式匹配请使用 matches()Pattern 类。

传入 null 时的行为

contains() 传入 null 会触发 NullPointerException
例如,下面的代码会抛出异常:

String text = null;
System.out.println(text.contains("test")); // Exception occurs

同样,如果目标字符串本身为 null,也会抛出相同的异常。
因此,强烈建议在调用 contains() 之前 进行空值检查

3. 实际使用示例与重要注意事项

Java 的 contains() 方法直观且方便,但不当使用可能导致意外的 bug 或低效代码。
本节将说明 contains() 的基本用法,并列出使用时需要留意的关键点。

3-1. 基本使用示例

下面的代码演示了检查目标字符串是否包含特定关键字的简单例子:

String sentence = "今日はJavaの勉強をしています。";

if (sentence.contains("Java")) {
    System.out.println("Javaが含まれています。");
} else {
    System.out.println("Javaは含まれていません。");
}

如示例所示,contains() 常与 if 语句结合使用,以实现条件分支。

3-2. 大小写敏感性

contains() 方法是 大小写敏感 的。
例如,下面的代码会返回 false

String text = "Welcome to Java";
System.out.println(text.contains("java")); // false

在这种情况下,通常会在比较前将字符串转换为小写(或大写)

String text = "Welcome to Java";
System.out.println(text.toLowerCase().contains("java")); // true

这种方法有助于消除输入大小写差异(例如,用户输入)。

3-3. null 和空字符串的处理

使用 contains() 时,最重要的考虑之一是 null 的处理。
如果目标字符串或参数为 null,则会发生 NullPointerException

String text = null;
System.out.println(text.contains("test")); // Runtime error

为了避免这个问题,请始终添加 null 检查:

if (text != null && text.contains("test")) {
    // Safe to process
}

另请注意:
传递空字符串 ("") 总是返回 true

String sample = "test";
System.out.println(sample.contains("")); // true

然而,这种行为在实践中很少有用,如果意外传递空字符串,可能会无意中导致 bug。

3-4. 不支持多关键字搜索

contains() 一次只能检查一个关键字。
要检查多个关键字,必须多次调用 contains() 或使用 Stream API。

String target = "エラーコード123:アクセス拒否";
if (target.contains("エラー") || target.contains("拒否")) {
    System.out.println("問題のあるメッセージです。");
}

或者,对于动态关键字集:

List<String> keywords = Arrays.asList("エラー", "障害", "失敗");
boolean found = keywords.stream().anyMatch(target::contains);

4. 经常与 contains() 比较的方法

Java 提供了几种用于比较字符串或检查特定子字符串是否存在的方法。
其中,contains() 用于“部分匹配”,但其他方法也服务于类似目的。
本节解释这些方法的特性和差异,以帮助您适当使用它们。

4-1. 与 equals() 的区别:完全匹配 vs. 部分匹配

equals() 判断两个字符串是否完全相同
相比之下,contains() 检查部分匹配。

String a = "Java";
String b = "Java";

System.out.println(a.equals(b));      // true: Exact match
System.out.println(a.contains("av")); // true: Partial match

主要区别:

Comparisonequals()contains()
Match TypeExact matchPartial match
Case SensitivitySensitiveSensitive
Argument TypeObjectCharSequence

使用指南:
当值必须完全匹配时使用 equals()(例如,ID 验证)。
当部分匹配可接受时使用 contains()(例如,关键字搜索)。

4-2. 与 indexOf() 的区别:是否需要位置

indexOf() 方法也可用于检查字符串中是否存在子字符串。
区别在于,如果找到,indexOf() 返回子字符串的起始索引
如果未找到子字符串,则返回 -1

String text = "Hello, Java World!";
System.out.println(text.indexOf("Java"));    // 7
System.out.println(text.indexOf("Python"));  // -1

您也可以使用 indexOf() 来复制 contains() 的行为:

if (text.indexOf("Java") >= 0) {
    System.out.println("It is contained.");
}

使用指南:
如果不需要索引,contains() 更具可读性且更可取。

4-3. 与 matches() 的区别:对正则表达式的支持

matches() 方法检查字符串是否完全匹配给定的正则表达式
相比之下,contains() 仅检查字面子字符串匹配,不支持 regex。

String text = "abc123";
System.out.println(text.matches(".*123")); // true
System.out.println(text.contains(".*123")); // false (not regex)

如果您想要基于 regex 的部分匹配,请使用 Pattern 类:

4-4. 特性比较总结

MethodPurposeReturn TypeRegex SupportUse Case
contains()Partial matchbooleanNoKeyword search
equals()Exact matchbooleanNoID/password checks
indexOf()Get match positionintNoIndex-based processing
matches()Regex matchbooleanYesFind pattern-based strings

5. 常见用例和示例代码

Java 的 contains() 方法简单却在实际开发场景中被广泛使用。
典型用例包括用户输入验证、日志分析和过滤操作。
本节涵盖带有相应代码的实际示例。

5-1. 用户输入验证(检测禁止词)

在表单或聊天应用中,您可能需要检测是否包含某些禁止词。

String input = "このアプリは最悪だ";
String banned = "最悪";

if (input.contains(banned)) {
    System.out.println("不適切な言葉が含まれています。");
}

处理多个 NG 词:

List<String> bannedWords = Arrays.asList("最悪", "バカ", "死ね");
for (String word : bannedWords) {
    if (input.contains(word)) {
        System.out.println("不適切な言葉が含まれています: " + word);
        break;
    }
}

5-2. 日志文件分析(检测特定消息)

在分析系统或应用日志时,您可能希望仅提取包含特定关键字如 ERROR 或 WARN 的行。

List<String> logs = Arrays.asList(
    "[INFO] サーバーが起動しました",
    "[ERROR] データベース接続失敗",
    "[WARN] メモリ使用率が高い"
);

for (String log : logs) {
    if (log.contains("ERROR")) {
        System.out.println("エラー発生ログ: " + log);
    }
}

5-3. 列表中字符串过滤(使用 Stream API)

在处理大型数据集时,使用 Stream API 提取仅包含特定子字符串的元素:

List<String> users = Arrays.asList("tanaka@example.com", "sato@gmail.com", "yamada@yahoo.co.jp");

List<String> gmailUsers = users.stream()
    .filter(email -> email.contains("@gmail.com"))
    .collect(Collectors.toList());

System.out.println(gmailUsers); // [sato@gmail.com]

5-4. 解析 HTTP 请求头或 URL

在 Web 开发中,路由或设备特定处理可能需要检查 User-Agent 或 URL 中的子字符串。

String userAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X)";
if (userAgent.contains("iPhone")) {
    System.out.println("スマートフォンからのアクセスです。");
}

5-5. 检查文件路径或扩展名

使用其路径确定文件类型:

String filePath = "/usr/local/data/sample.csv";
if (filePath.contains(".csv")) {
    System.out.println("CSVファイルです。");
}

注意:
对于文件扩展名检查,endsWith(".csv") 通常更准确。

实际考虑事项

  • 当需要准确性时,应用规范化(例如 toLowerCase()trim())。
  • 对于大规模数据,考虑 Stream API 或正则表达式。
  • 记住 contains() 是部分匹配——与其他条件结合以获得更安全的逻辑。

6. 性能考虑

虽然 contains() 方法提供了出色的可读性和简单性,但在处理大型数据集或运行重复操作时,您必须考虑其 性能影响
本节解释 contains() 的处理成本以及提高效率的替代方法。

6-1. contains() 的内部行为和时间复杂度

contains() 方法从开头 顺序搜索 目标字符串以定位子字符串。
内部,它依赖于 indexOf() 方法,其最坏情况时间复杂度为:

O(n * m)
– n = 目标字符串的长度
– m = 搜索字符串的长度

繁重处理的示例:

for (String line : hugeTextList) {
    if (line.contains("error")) {
        // processing
    }
}

这在大型循环中重复时会显著影响性能。

6-2. 在频繁搜索期间提高性能的技术

在大型数据集中重复使用 contains() 时,以下技术可以提高处理速度:

• 提前将所有字符串转换为小写

而不是在每次比较时调用 toLowerCase(),提前规范化字符串:

List<String> normalizedList = originalList.stream()
    .map(String::toLowerCase)
    .collect(Collectors.toList());
• 使用带有 parallel() 的 Stream API 进行并行处理

利用 CPU 核心来加速搜索:

List<String> result = hugeTextList.parallelStream()
    .filter(line -> line.contains("keyword"))
    .collect(Collectors.toList());
• 对于复杂搜索模式使用正则表达式

如果条件复杂且可以用一个正则表达式表示,Pattern 可能性能更好:

Pattern pattern = Pattern.compile("error|fail|fatal");
for (String log : logs) {
    if (pattern.matcher(log).find()) {
        // matched
    }
}

6-3. 内存效率和可重用性考虑

频繁转换字符串的操作——如 toLowerCase()substring()——可能会生成许多不必要的字符串对象,影响内存使用。
这对于长运行的应用或服务器端处理尤为重要。

关键点:

  • 避免创建不必要的字符串实例。
  • 对于大型数据集,考虑缓冲或分块处理。
  • 缓存重复的 contains() 结果在某些情况下可能提高性能。

7. 与其他编程语言的比较

Java 的 contains() 方法提供了简单、可靠的子字符串匹配,但其他语言也提供了类似的功能,各有特色。
本节比较 Python、JavaScript 和 C# 中的子字符串检查,以突出差异和相似性。

7-1. Python:使用 in 操作符进行简单部分匹配

在 Python 中,你可以使用 in 操作符检查子字符串包含:

text = "Hello, Python!"
if "Python" in text:
    print("含まれています")

这种语法极其易读——几乎像自然语言一样——并且学习成本最低。

差异和注意事项:

  • in 是语言操作符,而不是方法。
  • Python 的字符串比较也是区分大小写的。
  • None 会引发异常;需要进行 null 检查。

7-2. JavaScript:使用 includes() 进行部分匹配

在 JavaScript(ES6+)中,你可以使用 includes() 方法:

const text = "JavaScript is fun";
console.log(text.includes("fun")); // true

此方法与 Java 的 contains() 非常相似,便于在脑海中迁移。

差异和注意事项:

  • 传递 undefined 不会抛出异常;它只是返回 false
  • includes() 也适用于数组,提高了其多功能性。

7-3. C#:类似于 Java 的 Contains()

C# 也提供了行为类似于 Java 的 Contains() 方法:

string text = "Welcome to C#";
bool result = text.Contains("C#");

差异和注意事项:

  • C# 的 Contains() 默认区分大小写,但你可以使用 StringComparison.OrdinalIgnoreCase 忽略大小写。
  • 传递 null 会触发 ArgumentNullException

7-4. 跨语言比较表

LanguageExample SyntaxCase SensitivityNotes
Java"abc".contains("a")SensitiveThrows exception on null
Python"a" in "abc"SensitiveMost intuitive syntax
JavaScript"abc".includes("a")SensitiveAlso works for arrays
C#"abc".Contains("a")Sensitive (configurable)Comparison mode can be chosen

总结:为你的用例选择合适的语法

尽管子字符串检查是跨语言的常见需求,但每种语言都提供了自己的方法或语法。
Java 的 contains() 提供了稳定性和清晰度,使其非常适合企业系统和可维护的应用。
像 Python 和 JavaScript 这样的语言提供了更简单、更简洁的语法,这对于轻量级脚本或快速原型开发非常理想。

通过理解共同概念以及每种语言的具体特性,你将能够跨各种语言编写更安全、更高效的代码。

8. 常见问题解答 (FAQ)

以下是关于 Java 的 contains() 方法的常见问题——帮助你理解棘手点并避免常见陷阱。

Q1. contains() 是否区分大小写?

是的,它是区分大小写的。
例如,"Java".contains("java") 返回 false

解决方案:

String input = "Welcome to Java";
boolean result = input.toLowerCase().contains("java");

Q2. 如何使用正则表达式检查部分匹配?

contains() 不支持正则表达式
使用 matches()Pattern 类代替。

示例(检查数字模式):

import java.util.regex.Pattern;
import java.util.regex.Matcher;

String text = "注文番号: A123456";
Pattern pattern = Pattern.compile("A\\d+");
Matcher matcher = pattern.matcher(text);

if (matcher.find()) {
    System.out.println("パターンに一致しました。");
}

Q3. 如果对 null 调用 contains() 会怎样?

会抛出 NullPointerException。

String target = null;
System.out.println(target.contains("test")); // Error

解决方案:

if (target != null && target.contains("test")) {
    System.out.println("含まれています。");
}

Q4. 如果向 contains() 传入空字符串(””)会怎样?

它总是返回 true。

String text = "Java";
System.out.println(text.contains("")); // true

虽然这是官方规范的一部分,但此行为很少有用,如果不打算使用空字符串,可能会导致意外的错误。

Q5. contains() 能一次搜索多个关键字吗?

不能。每次调用只能检查一个关键字。

String text = "本日はシステムエラーが発生しました";
if (text.contains("エラー") || text.contains("障害") || text.contains("失敗")) {
    System.out.println("問題が検出されました。");
}

动态方法:

List<String> keywords = Arrays.asList("エラー", "障害", "失敗");
boolean found = keywords.stream().anyMatch(text::contains);

Q6. 何时应该使用 contains() 而不是 indexOf()

contains() 返回布尔值,而 indexOf() 返回数值索引。

  • 当你只想知道子串是否存在时使用 contains()
  • 当你还需要知道子串位置时使用 indexOf()
    String text = "Error: Disk full";
    if (text.contains("Error")) {
        int pos = text.indexOf("Error");
        System.out.println("Position: " + pos);
    }
    

9. 结论

Java 的 contains() 方法是一个强大且便利的工具,用于判断特定子串是否 包含在字符串中
它在用户输入验证、日志分析和数据过滤等各种场景中被广泛使用。

本文涵盖了以下内容:

  • 基本语法和返回值
  • 大小写敏感性及其处理方法
  • 处理 null 和空字符串
  • 与其他字符串比较方法的区别
  • 实际用例:验证、日志搜索、Stream 处理
  • 性能考虑和优化技术
  • 与 Python、JavaScript 和 C# 的比较
  • 常见问题及排错技巧

虽然 contains() 直观且多功能,但在涉及 大数据集频繁调用复杂搜索条件 的情况下应慎重使用。
通过结合规范化、并行处理、正则表达式和缓存策略,你可以兼顾性能和可读性。

由于 contains() 是 Java 字符串处理的基础,我们希望本文能帮助你在开发项目中更安全、更高效地使用它。