1. 概要

このチュートリアルでは、 String にサブストリングが含まれているかどうかを確認するいくつかの方法を確認し、それぞれのパフォーマンスを比較します。

2. String.indexOf

まず、String.indexOfメソッドを使用してみましょう。 indexOfは、部分文字列が見つかった最初の位置を示します。まったく見つからない場合は-1を示します。

「Rhap」を検索すると、9が返されます。

Assert.assertEquals(9, "Bohemian Rhapsodyan".indexOf("Rhap"));

「rhap」を検索すると、大文字と小文字が区別されるため、-1が返されます。

Assert.assertEquals(-1, "Bohemian Rhapsodyan".indexOf("rhap"));
Assert.assertEquals(9, "Bohemian Rhapsodyan".toLowerCase().indexOf("rhap"));

substring “ an”を検索すると、最初のオカレンスが返されるため、6が返されることに注意することも重要です。

Assert.assertEquals(6, "Bohemian Rhapsodyan".indexOf("an"));

3. String.contains

次に、String.containsを試してみましょう。 containsは、文字列全体でサブ文字列を検索し、見つかった場合はtrueを返し、それ以外の場合はfalseを返します。

この例では、「Hey」が見つかったため、containstrueを返します。

Assert.assertTrue("Hey Ho, let's go".contains("Hey"));

文字列が見つからない場合、containsfalseを返します。

Assert.assertFalse("Hey Ho, let's go".contains("jey"));

最後の例では、 String.containsで大文字と小文字が区別されるため、「hey」は見つかりません。

Assert.assertFalse("Hey Ho, let's go".contains("hey"));
Assert.assertTrue("Hey Ho, let's go".toLowerCase().contains("hey"));

興味深い点は、にはindexOf の内部呼び出しが含まれており、substringが含まれているかどうかを知ることです。

4. StringUtils.containsIgnoreCase

3番目のアプローチは、Apache CommonsLangライブラリStringUtils#containsIgnoreCaseを使用することです。

Assert.assertTrue(StringUtils.containsIgnoreCase("Runaway train", "train"));
Assert.assertTrue(StringUtils.containsIgnoreCase("Runaway train", "Train"));

大文字と小文字を無視して、が文字列にサブ文字列が含まれているかどうかをチェックすることがわかります。 そのため、「暴走列車」内で「Trai」と「trai」を検索すると、containsIgnoreCasetrueを返します。

このアプローチは、ケースを無視するために追加の時間がかかるため、以前のアプローチほど効率的ではありません。 containsIgnoreCase は、すべての文字を内部で大文字に変換し、元の文字ではなく変換された文字を比較します。

5. パターンを使用する

最後のアプローチは、正規表現パターンを使用することです。

Pattern pattern = Pattern.compile("(?<!\\S)" + "road" + "(?!\\S)");

最初にパターンを作成し、次にマッチャーを作成し、最後に、部分文字列が出現するかどうかをfindメソッドで確認する必要があることがわかります。

Matcher matcher = pattern.matcher("Hit the road Jack");
Assert.assertTrue(matcher.find());

たとえば、 find を初めて実行すると、「Hit the road Jack」という文字列の中に「road」という単語が含まれているため、 true が返されますが、文字列内で同じ単語を見つけると、「もう戻ってこないでください」と返されます false:

Matcher matcher = pattern.matcher("and don't you come back no more");
Assert.assertFalse(matcher.find());

6. パフォーマンスの比較

Java Microbenchmark Harness (JMH)と呼ばれるオープンソースのマイクロベンチマークフレームワークを使用して、実行時間の観点から最も効率的な方法を決定します。

6.1. ベンチマークの設定

すべてのJMHベンチマークと同様に、ベンチマークを実行する前に特定のものを配置するために、setupメソッドを作成することができます。

@Setup
public void setup() {
    message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, " + 
      "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. " + 
      "Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris " + 
      "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in " + 
      "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. " + 
      "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt " + 
      "mollit anim id est laborum";
    pattern = Pattern.compile("(?<!\\S)" + "eiusmod" + "(?!\\S)");
}

setup メソッドでは、messageフィールドを初期化しています。 これを、さまざまな検索実装のソーステキストとして使用します。

また、後でベンチマークの1つで使用するために、patternを初期化しています。

6.2. String.indexOfベンチマーク

最初のベンチマークでは、indexOfを使用します。

@Benchmark
public int indexOf() {
    return message.indexOf("eiusmod");
}

message変数のどの位置に「eiusmod」が存在するかを検索します。

6.3. String.containsベンチマーク

2番目のベンチマークでは、containsを使用します。

@Benchmark
public boolean contains() {
    return message.contains("eiusmod");
}

メッセージの値に、前のベンチマークで使用されたのと同じサブストリングである「eiusmod」が含まれているかどうかを調べます。

6.4. StringUtils.containsIgnoreCaseベンチマーク

3番目のベンチマークでは、 StringUtils# containsIgnoreCaseを使用します。

@Benchmark
public boolean containsStringUtilsIgnoreCase() {
    return StringUtils.containsIgnoreCase(message, "eiusmod");
}

以前のベンチマークと同様に、message値でsubstringを検索します。

6.5. パターンベンチマーク

そして、最後のベンチマークではPatternを使用します。

@Benchmark
public boolean searchWithPattern() {
    return pattern.matcher(message).find();
}

setup メソッドで初期化されたパターンを使用して、 Matcher を作成し、以前と同じサブストリングを使用してfindメソッドを呼び出すことができます。

6.6. ベンチマーク結果の分析

ベンチマーク結果をナノ秒単位で評価していることに注意してください

JMHテストを実行した後、それぞれにかかった平均時間を確認できます。

  • にはが含まれます:14.736 ns
  • indexOf :14.200 ns
  • containsStringUtilsIgnoreCase :385.632 ns
  • searchWithPattern :1014.633 ns

indexOf メソッドが最も効率的なメソッドであり、containsがそれに続きます。 indexOfを内部的に使用しているため、containsに時間がかかったのは理にかなっています。

containsStringUtilsIgnoreCase は大文字と小文字を区別しないため、以前のものと比較して余分な時間がかかりました。

searchWithPattern は、前回の平均時間がさらに長くなり、パターンの使用がこのタスクの最悪の代替手段であることを証明しました。

7. 結論

この記事では、次の部分文字列を検索するさまざまな方法について説明しました。 弦。 また、さまざまなソリューションのパフォーマンスのベンチマークも行いました。

いつものように、コードはGitHubから入手できます。