查找Java中两个字符串之间的差异

Finding the Difference Between Two Strings in Java

1.概述

本快速教程将展示如何使用Java查找两个字符串之间的差异。

在本教程中,我们将使用两个现有的Java库,并比较它们解决此问题的方法。

2.问题

让我们考虑以下要求:我们想找到字符串" ABCDELMN"和" ABCFGLMN"之间的差异。

根据我们需要的输出格式,并忽略编写自定义代码的可能性,我们发现了两个主要选项。

第一个是Google编写的名为diff-match-patch的库。正如他们所声称的,该库提供了用于同步纯文本的强大算法。

另一个选项是Apache Commons Lang的StringUtilsclass。

让我们探讨一下两者之间的区别。

3. diff-match-patch

出于本文的目的,我们将使用原始Google库的一个分支,因为原始库的构件未在Maven Central中发布。另外,某些类名称与原始代码库不同,并且更符合Java标准。

首先,我们需要在我们的pom.xml文件中包含其依赖项:

1
2
3
4
5
<dependency>
    <groupId>org.bitbucket.cowwoc</groupId>
    diff-match-patch</artifactId>
    <version>1.2</version>
</dependency>

然后,让我们考虑以下代码:

1
2
3
4
String text1 ="ABCDELMN";
String text2 ="ABCFGLMN";
DiffMatchPatch dmp = new DiffMatchPatch();
LinkedList<Diff> diff = dmp.diffMain(text1, text2, false);

如果运行上面的代码(这会在text1和text2之间产生差异),则打印变量diff将产生以下输出:

1
[Diff(EQUAL,"ABC"), Diff(DELETE,"DE"), Diff(INSERT,"FG"), Diff(EQUAL,"LMN")]

实际上,输出将是Diff对象的列表,每个对象由一种操作类型(INSERT,DELETE或EQUAL)以及与该操作相关联的文本部分组成。

当运行text2和text1之间的差异时,我们将得到以下结果:

1
[Diff(EQUAL,"ABC"), Diff(DELETE,"FG"), Diff(INSERT,"DE"), Diff(EQUAL,"LMN")]

4. StringUtils

Apache Commons中的类具有更简单的方法。

首先,我们将Apache Commons Lang依赖项添加到我们的pom.xml文件中:

1
2
3
4
5
<dependency>
    <groupId>org.apache.commons</groupId>
    commons-lang3</artifactId>
    <version>3.9</version>
</dependency>

然后,要使用Apache Commons查找两个文本之间的差异,我们将其称为StringUtils#Difference:

1
StringUtils.difference(text1, text2)

产生的输出将是一个简单的字符串:

1
FGLMN

而运行text2和text1之间的差异将返回:

1
DELMN

可以使用StringUtils.indexOfDifference()增强此简单方法,该函数将返回两个字符串开始不同的索引(在本例中为该字符串的第四个字符)。该索引可用于获取原始字符串的子字符串,以显示两个输入之间的共同点以及区别。

5.表现

对于我们的基准测试,我们生成了10,000个字符串列表,其中固定部分包含10个字符,然后是20个随机字母字符。

然后,我们遍历列表,并在列表的第n个元素和第n + 1个元素之间进行比较:

1
2
3
4
5
6
7
@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();
}

1
2
3
4
5
6
7
@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();
}

最后,让我们运行基准测试并比较两个库:

1
2
3
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

六,结论

就纯执行速度而言,尽管StringUtils仅返回两个字符串开始有所不同的子字符串,但显然性能更高。

同时,Diff-Match-Patch提供更全面的比较结果,但会降低性能。

这些示例和代码片段的实现可从GitHub上获得。