精通 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 時的行為

null 傳給 contains() 會拋出 NullPointerException
例如,以下程式碼會產生例外:

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

同樣地,若目標字串本身為 null,也會拋出相同的例外。
因此,強烈建議在呼叫 contains() 之前 先做 null 檢查

3. 實務使用範例與重要注意事項

Java 的 contains() 方法直觀且方便,但不當使用可能導致意外的錯誤或低效的程式碼。
本節說明 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

使用指引:
– 必須完全相同時(例如 ID 驗證),使用 equals()
– 只要部分匹配即可(例如關鍵字搜尋),使用 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() 只檢查字面子字串,並不支援正規表達式。

String text = "abc123";
System.out.println(text.matches(".*123")); // true
System.out.println(text.contains(".*123")); // false (not 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#:Contains() 與 Java 類似

C# 同樣提供 Contains() 方法,行為與 Java 相近:

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() 是否區分大小寫?

是的,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 字串操作的基礎,我們期望本文能協助你在開發專案中更安全、有效地使用它。