Перейти к основному содержимому

Нахождение разницы между двумя строками в Java

· 3 мин. чтения

1. Обзор

В этом кратком руководстве показано, как найти разницу между двумя строками с помощью Java.

В этом уроке мы будем использовать две существующие библиотеки Java и сравним их подходы к решению этой проблемы.

2. Проблема

Рассмотрим следующее требование: мы хотим найти разницу между строками « ABCDELMN» и «ABCFGLMN».

В зависимости от того, в каком формате нам нужен вывод, и игнорируя возможность написать собственный код для этого, мы нашли два основных доступных варианта.

Первая — это написанная Google библиотека под названием diff-match-patch . Как они утверждают, библиотека предлагает надежные алгоритмы синхронизации простого текста .

Другой вариант — класс StringUtils из Apache Commons Lang.

Давайте рассмотрим различия между этими двумя.

3. diff-match-patch

Для целей этой статьи мы будем использовать форк оригинальной библиотеки Google , так как артефакты для оригинальной не выпускаются на Maven Central. Кроме того, некоторые имена классов отличаются от исходной кодовой базы и больше соответствуют стандартам Java.

Во-первых, нам нужно включить его зависимость в наш файл pom.xml :

<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);

Если мы запустим приведенный выше код, который создает разницу между текстом1 и текстом2 , печать переменной diff приведет к следующему результату:

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

На самом деле на выходе будет список объектов Diff , каждый из которых формируется типом операции ( INSERT , DELETE или EQUAL ), и частью текста, связанной с операцией .

При запуске diff между text2 и text1 мы получим такой результат:

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

4. Строковые утилиты

Класс от Apache Commons имеет более упрощенный подход .

Во- первых, мы добавим зависимость Apache Commons Lang в наш файл pom.xml :

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>

Затем, чтобы найти разницу между двумя текстами с помощью Apache Commons, мы вызываем StringUtils#Difference :

StringUtils.difference(text1, text2)

Результатом будет простая строка :

FGLMN

В то время как запуск diff между text2 и text1 вернет:

DELMN

Этот простой подход можно улучшить с помощью StringUtils.indexOfDifference() , который вернет индекс, с которого две строки начинают различаться (в нашем случае это четвертый символ строки). Этот индекс можно использовать для получения подстроки исходной строки , чтобы показать, что общего между двумя входными данными , в дополнение к тому, что отличается.

5. Производительность

Для наших тестов мы генерируем список из 10 000 строк с фиксированной частью из 10 символов , за которыми следуют 20 случайных буквенных символов .

Затем мы перебираем список и выполняем сравнение между n -м элементом и n+1 -м элементом списка:

@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();
}

Наконец, давайте запустим тесты и сравним две библиотеки:

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. Заключение

С точки зрения чистой скорости выполнения StringUtils явно производительнее , хотя и возвращает только ту подстроку, с которой начинают различаться две строки.

В то же время Diff-Match-Patch обеспечивает более тщательный результат сравнения , за счет производительности.

Реализация этих примеров и фрагментов доступна на GitHub .