Javaで2つの文字列の違いを見つける

1. 概要

このクイックチュートリアルでは、Javaを使用して2つの文字列の違いを見つける方法を示します。
このチュートリアルでは、* 2つの既存のJavaライブラリ*を使用して、この問題に対するアプローチを比較します。

2. 問題

次の要件を考えてみましょう。文字列__“__ABCDELMN“ABCFGLMNâ€の違いを見つけたいと思います。
出力が必要なフォーマットに応じて、カスタムコードを記述する可能性を無視して、2つの主なオプションが利用可能であることがわかりました。
最初のものは、Google_https://github.com/google/diff-match-patch [diff-match-patch]と呼ばれるGoogleによって作成されたライブラリです。_彼らが主張するように、このライブラリは*プレーンテキストを同期するための堅牢なアルゴリズム*を提供します。
もう1つのオプションは、Apache Commons Langの__https://github.com/apache/commons-lang/blob/master/src/main/java/org/apache/commons/lang3/StringUtils.java [StringUtils] __classです。
これら2つの違いを調べてみましょう。

*3. diff-match-patch *

この記事では、https://search.maven.org/search?q = org.bitbucket.cowwoc%20diff-match-patch [元のGoogleライブラリのフォーク]をアーティファクトとして使用しますオリジナルのものはMaven Centralでリリースされていません。 また、一部のクラス名は元のコードベースとは異なり、Java標準により忠実です。
最初に、依存関係を__pom.xml __fileに含める必要があります。
<dependency>
    <groupId>org.bitbucket.cowwoc</groupId>
    <artifactId>diff-match-patch</artifactId>
    <version>1.2</version>
</dependency>
次に、このコードを考えてみましょう。
String text1 = "ABCDELMN";
String text2 = "ABCFGLMN";
DiffMatchPatch dmp = new DiffMatchPatch();
LinkedList<Diff> diff = dmp.diffMain(text1, text2, false);
_text1_と_text2_の差を生成する上記のコードを実行すると、変数_diff_を出力すると次の出力が生成されます。
[Diff(EQUAL,"ABC"), Diff(DELETE,"DE"), Diff(INSERT,"FG"), Diff(EQUAL,"LMN")]
実際、出力は* Diff_オブジェクトのリスト*になり、各オブジェクトは*操作タイプ*(_INSERT _、_ DELETE_または_EQUAL_)によって形成され、*操作に関連付けられたテキストの一部*になります。
_text2_と_text1、_の差分を実行すると、次の結果が得られます。
[Diff(EQUAL,"ABC"), Diff(DELETE,"FG"), Diff(INSERT,"DE"), Diff(EQUAL,"LMN")]

*4. StringUtils *

_Apache Commons_のクラスには、*もっと単純なアプローチ*があります。
まず、https://search.maven.org/search?q = g:org.apache.commons%20AND%20a:commons-lang3 [Apache Commons Lang依存関係]を__pom.xml __fileに追加します。
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>3.9</version>
</dependency>
次に、Apache Commonsで2つのテキストの違いを見つけるために、_StringUtils#Difference_を呼び出します。
StringUtils.difference(text1, text2)
生成される出力は、*単純な文字列*になります:
FGLMN
_text2_と_text1_の差分を実行すると、以下が返されます。
DELMN
この単純なアプローチは、* * _StringUtils.indexOfDifference()_ *を使用して拡張できます。これは、* 2つの文字列が異なる開始インデックス*(この場合、文字列の4番目の文字)を返します。 このインデックスを使用して、元の文字列の部分文字列を取得し、異なるものに加えて、* 2つの入力に共通するもの*を表示できます。

5. パフォーマンス

ベンチマークでは、* 10文字の固定部分*に続いて* 20のランダムなアルファベット文字*を含む10,000個の文字列のリストを生成します。
次に、リストをループして、リストの_n ^ th ^ _要素と_n 1 ^ th ^ _要素の差分を実行します。
@Benchmark
public int diffMatchPatch() {
    for (int i = 0; i < inputs.size() - 1; i++) {
        diffMatchPatch.diffMain(inputs.get(i), inputs.get(i + 1), false);
    }
    return inputs.size();
}
@Benchmark
public int stringUtils() {
    for (int i = 0; i < inputs.size() - 1; i++) {
        StringUtils.difference(inputs.get(i), inputs.get(i + 1));
    }
    return inputs.size();
}
最後に、ベンチマークを実行して、2つのライブラリを比較しましょう。
Benchmark                                   Mode  Cnt    Score   Error  Units
StringDiffBenchmarkUnitTest.diffMatchPatch  avgt   50  130.559 ± 1.501  ms/op
StringDiffBenchmarkUnitTest.stringUtils     avgt   50    0.211 ± 0.003  ms/op

6. 結論

純粋な実行速度の観点では、* _ St​​ringUtils_は明らかにパフォーマンスが高い*ですが、2つの文字列が異なる部分文字列のみを返します。
同時に、_Diff-Match-Patch_は、パフォーマンスを犠牲にして*より徹底的な比較結果*を提供します。
これらの例とスニペットの実装は、https://github.com/eugenp/tutorials/tree/master/java-strings-3 [GitHub上]で入手できます。