java中如何以不區分大小寫的方式檢查一個字符串中包含另外一個字符串|Java Debug 筆記

**本文正在參加「Java主題月 - Java Debug筆記活動」,詳情查看 活動連接 **html

問題:java

我有兩個字符串以下:正則表達式

String s1 = "AbBaCca";
String s2 = "bac";
複製代碼

我想要檢查s2包含了s1能夠這樣寫:apache

return s1.contains(s2);
複製代碼

我很確定contains()方法是區分大小寫的,我能夠經過官方文檔肯定這一點。api

基於這個緣由我能想到解決問題最好的方式是這樣:數組

return s1.toLowerCase().contains(s2.toLowerCase());
複製代碼

除開這種方法,是否有其它(更好)的方式來實現不區分大小寫匹配呢呢?緩存

回答1(支持票數332):markdown

是的,contains()方法要區分大小寫,你可使用java.util.regex.Pattern 類的  CASE_INSENSITIVE 標記 來進行不區分大小寫的匹配:oracle

Pattern.compile(wantedStr, Pattern.CASE_INSENSITIVE).matcher(source).find();
複製代碼

回答2(支持票數276):工具

s2包含了正則表達式標記例如\d時回答1的答案就會有問題。

這種狀況下應該對s2使用 Pattern.quote()方法

Pattern.compile(Pattern.quote(s2), Pattern.CASE_INSENSITIVE).matcher(s1).find();
複製代碼

回答3(支持票數180):

你可使用:

org.apache.commons.lang3.StringUtils.containsIgnoreCase("AbBaCca", "bac");
複製代碼

這個apache的工具類是很用的,並且性能可能比你寫的正則表達式更好

回答4(支持票數129):

有一個更快的實現方式:利用String.regionMatches()

使用正則表達式可能會相對比較慢。若是你只想檢查一種狀況慢一點沒有關係,

可是若是你的數組或集合有成千上萬條字符串,那麼匹配就會變得很是慢。

下面給出的解決方案沒有使用正則表達式和toLowerCase()方法(這樣之因此慢是由於它生成了新的字符串,而後在檢查完後就銷燬掉)

這個解決方案是基於**[String.regionMatches()](http://docs.oracle.com/javase/8/docs/api/java/lang/String.html#regionMatches-boolean-int-java.lang.String-int-int-)**這個看上去沒見過的方法。它是用於檢查兩個字符串是否匹配,更重要的是這個方法有一個便於重載的ignoreCase參數

public static boolean containsIgnoreCase(String src, String what) {
    final int length = what.length();
    if (length == 0)
        return true; // 包含空字符串

    final char firstLo = Character.toLowerCase(what.charAt(0));
    final char firstUp = Character.toUpperCase(what.charAt(0));

    for (int i = src.length() - length; i >= 0; i--) {
        // Quick check before calling the more expensive regionMatches() method:
        final char ch = src.charAt(i);
        if (ch != firstLo && ch != firstUp)
            continue;

        if (src.regionMatches(true, i, what, 0, length))
            return true;
    }

    return false;
}
複製代碼

速度分析:

速度分析並不一意味着要造火箭,這裏僅僅是展現每種方法的速度曲線圖。

我比較了5種方法:

1.本身寫的**containsIgnoreCase()方法**

2.將字符串轉換成小寫並調用String.contains()方法

3.將源字符串轉換成小寫緩存起來,而後調用String.contains()方法.這種解決方案已經不夠靈活,由於它只能測試預先設定好的字符串

4.使用正則表達式(用這個Pattern.compile().matcher().find()..)

5.提早建立正則表達式和緩存Pattern.這種解決方案已經不夠靈活,由於它只能測試預先設定好的字符串

速度測試結果(每一個方法均調用1000萬次):

1.第1種方法:670ms

2.第2種方法:2x toLowerCase() and contains(): 2829 ms

3.第3種方法:1x toLowerCase() and contains() with cached substring: 2446 ms

4.第4種方法:7180ms

5.第5種方法:Regexp with cached Pattern:1845ms

作成一個表格:

RELATIVE SPEED   1/RELATIVE SPEED
 METHOD                          EXEC TIME    TO SLOWEST      TO FASTEST (#1)
------------------------------------------------------------------------------
 1. Using regionMatches()          670 ms       10.7x            1.0x
 2. 2x lowercase+contains         2829 ms        2.5x            4.2x
 3. 1x lowercase+contains cache   2446 ms        2.9x            3.7x
 4. Regexp                        7180 ms        1.0x           10.7x
 5. Regexp+cached pattern         1845 ms        3.9x            2.8x
複製代碼

第1種方法的速度是第二、3種方法的四倍,第4種方法的十倍,第5種方法的3倍

用於速度分析的測試代碼以下:

import java.util.regex.Pattern;

public class ContainsAnalysis {

    // Case 1 utilizing String.regionMatches()
    public static boolean containsIgnoreCase(String src, String what) {
        final int length = what.length();
        if (length == 0)
            return true; // Empty string is contained

        final char firstLo = Character.toLowerCase(what.charAt(0));
        final char firstUp = Character.toUpperCase(what.charAt(0));

        for (int i = src.length() - length; i >= 0; i--) {
            // Quick check before calling the more expensive regionMatches()
            // method:
            final char ch = src.charAt(i);
            if (ch != firstLo && ch != firstUp)
                continue;

            if (src.regionMatches(true, i, what, 0, length))
                return true;
        }

        return false;
    }

    // Case 2 with 2x toLowerCase() and contains()
    public static boolean containsConverting(String src, String what) {
        return src.toLowerCase().contains(what.toLowerCase());
    }

    // The cached substring for case 3
    private static final String S = "i am".toLowerCase();

    // Case 3 with pre-cached substring and 1x toLowerCase() and contains()
    public static boolean containsConverting(String src) {
        return src.toLowerCase().contains(S);
    }

    // Case 4 with regexp
    public static boolean containsIgnoreCaseRegexp(String src, String what) {
        return Pattern.compile(Pattern.quote(what), Pattern.CASE_INSENSITIVE)
                    .matcher(src).find();
    }

    // The cached pattern for case 5
    private static final Pattern P = Pattern.compile(
            Pattern.quote("i am"), Pattern.CASE_INSENSITIVE);

    // Case 5 with pre-cached Pattern
    public static boolean containsIgnoreCaseRegexp(String src) {
        return P.matcher(src).find();
    }

    // Main method: perfroms speed analysis on different contains methods
    // (case ignored)
    public static void main(String[] args) throws Exception {
        final String src = "Hi, I am Adam";
        final String what = "i am";

        long start, end;
        final int N = 10_000_000;

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsIgnoreCase(src, what);
        end = System.nanoTime();
        System.out.println("Case 1 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsConverting(src, what);
        end = System.nanoTime();
        System.out.println("Case 2 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsConverting(src);
        end = System.nanoTime();
        System.out.println("Case 3 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsIgnoreCaseRegexp(src, what);
        end = System.nanoTime();
        System.out.println("Case 4 took " + ((end - start) / 1000000) + "ms");

        start = System.nanoTime();
        for (int i = 0; i < N; i++)
            containsIgnoreCaseRegexp(src);
        end = System.nanoTime();
        System.out.println("Case 5 took " + ((end - start) / 1000000) + "ms");
    }

}
複製代碼
相關文章
相關標籤/搜索