Javaで文字列からストップワードを削除する

1. 概要

このチュートリアルでは、Javaの_String_からストップワードを削除するさまざまな方法について説明します。 これは、オンラインサイトのユーザーによって追加されたコメントやレビューなど、テキストから不要または許可されていない単語を削除する場合に便利な操作です。
単純なループ_Collection.removeAll()_と正規表現を使用します。
最後に、https://www.baeldung.com/java-microbenchmark-harness [Java Microbenchmark Harness]を使用してパフォーマンスを比較します。

2. ストップワードの読み込み

最初に、テキストファイルからストップワードを読み込みます。
ここには、_I _、_ he _、_ she _、_ the_など、ストップワードと見なされる単語のリストを含む_english_stopwords.txt_ファイルがあります。
_Files.readAllLines()_を使用して、ストップワードを_String_の_List_にロードします。
@BeforeClass
public static void loadStopwords() throws IOException {
    stopwords = Files.readAllLines(Paths.get("english_stopwords.txt"));
}

3. ストップワードを手動で削除する

最初の解決策として、*各単語を反復処理してストップワードであるかどうかを確認することにより、ストップワードを手動で削除します*:
@Test
public void whenRemoveStopwordsManually_thenSuccess() {
    String original = "The quick brown fox jumps over the lazy dog";
    String target = "quick brown fox jumps lazy dog";
    String[] allWords = original.toLowerCase().split(" ");

    StringBuilder builder = new StringBuilder();
    for(String word : allWords) {
        if(!stopwords.contains(word)) {
            builder.append(word);
            builder.append(' ');
        }
    }

    String result = builder.toString().trim();
    assertEquals(result, target);
}

*4. Collection.removeAll() *の使用

次に、_String_の各単語を反復処理する代わりに、_Collection.removeAll()_を使用してすべてのストップワードを一度に削除できます*:
@Test
public void whenRemoveStopwordsUsingRemoveAll_thenSuccess() {
    ArrayList<String> allWords =
      Stream.of(original.toLowerCase().split(" "))
            .collect(Collectors.toCollection(ArrayList<String>::new));
    allWords.removeAll(stopwords);

    String result = allWords.stream().collect(Collectors.joining(" "));
    assertEquals(result, target);
}
この例では、_String_を単語の配列に分割した後、それを_ArrayList_に変換して、_removeAll()_メソッドを適用できるようにします。

5. 正規表現を使用する

最後に、*ストップワードリストから正規表現を作成し、それを使用して_String_内のストップワードを置換できます。
@Test
public void whenRemoveStopwordsUsingRegex_thenSuccess() {
    String stopwordsRegex = stopwords.stream()
      .collect(Collectors.joining("|", "\\b(", ")\\b\\s?"));

    String result = original.toLowerCase().replaceAll(stopwordsRegex, "");
    assertEquals(result, target);
}
結果の_stopwordsRegex_の形式は「\\ b(he | she | the |…)\\ b \\ s?」になります。 この正規表現では、「\ b」は「heat」の「he」の置き換えを避けるための単語境界を示し、「\ s?」はストップワードの置き換え後に余分なスペースを削除するためのゼロまたは1つのスペースを指します。

6. 性能比較

次に、どのメソッドが最高のパフォーマンスを発揮するかを見てみましょう。
まず、*ベンチマークを設定しましょう*。 _String_のソースとして、_shakespeare-hamlet.txt_と呼ばれるかなり大きなテキストファイルを使用します。
@Setup
public void setup() throws IOException {
    data = new String(Files.readAllBytes(Paths.get("shakespeare-hamlet.txt")));
    data = data.toLowerCase();
    stopwords = Files.readAllLines(Paths.get("english_stopwords.txt"));
    stopwordsRegex = stopwords.stream().collect(Collectors.joining("|", "\\b(", ")\\b\\s?"));
}
次に、_removeManually()_で始まるベンチマークメソッドを作成します。
@Benchmark
public String removeManually() {
    String[] allWords = data.split(" ");
    StringBuilder builder = new StringBuilder();
    for(String word : allWords) {
        if(!stopwords.contains(word)) {
            builder.append(word);
            builder.append(' ');
        }
    }
    return builder.toString().trim();
}
次に、_removeAll()_ベンチマークがあります。
@Benchmark
public String removeAll() {
    ArrayList<String> allWords =
      Stream.of(data.split(" "))
            .collect(Collectors.toCollection(ArrayList<String>::new));
    allWords.removeAll(stopwords);
    return allWords.stream().collect(Collectors.joining(" "));
}
最後に、_replaceRegex()_のベンチマークを追加します。
@Benchmark
public String replaceRegex() {
    return data.replaceAll(stopwordsRegex, "");
}
ベンチマークの結果は次のとおりです。
Benchmark                           Mode  Cnt   Score    Error  Units
removeAll                           avgt   60   7.782 ±  0.076  ms/op
removeManually                      avgt   60   8.186 ±  0.348  ms/op
replaceRegex                        avgt   60  42.035 ±  1.098  ms/op
  • _Collection.removeAll()_を使用すると実行時間が最も速くなり、正規表現を使用すると最も遅くなります*。

7. 結論

この簡単な記事では、Javaの_String_からストップワードを削除するさまざまな方法を学びました。 また、ベンチマークを行って、どのメソッドが最高のパフォーマンスを発揮するかを確認しました。
サンプルの完全なソースコードは、https://github.com/eugenp/tutorials/tree/master/java-strings-2 [GitHub上]で入手できます。