Beim Vergleich von Texten, wie Quellcode, Konfigurationsdateien oder anderen textbasierten Inhalten, ist es oft notwendig, die Unterschiede (engl. diffs) zwischen zwei Versionen zu ermitteln. Genau hier setzt die Bibliothek Java-Diff-Utils an. Sie stellt eine einfache, aber leistungsfähige API bereit, um Unterschiede zwischen Texten zu analysieren, darzustellen und zu verarbeiten. In diesem Artikel betrachten wir die Funktionsweise, Einsatzmöglichkeiten und Beispiele dieser Bibliothek im Detail.
Was ist Java-Diff-Utils?
Java-Diff-Utils ist eine Open-Source-Java-Bibliothek zur Berechnung von Differenzen zwischen zwei Textlisten. Sie basiert auf dem LCS-Algorithmus (Longest Common Subsequence), der auch bei klassischen diff-Tools zum Einsatz kommt. Die Bibliothek unterstützt sowohl reine Berechnung der Differenzen als auch deren Darstellung in verschiedenen Formaten wie Unified-Diff oder Inline-Darstellungen.
Die Bibliothek ist besonders hilfreich in Anwendungen wie:
- Versionskontrollsystemen
- Merge-Tools
- Editoren mit Änderungsverfolgung
- Unit-Testing von Textausgaben
- Synchronisationswerkzeugen
Installation
Java-Diff-Utils ist über Maven Central verfügbar. Für Maven-Projekte genügt folgender Eintrag:
<dependency>
<groupId>io.github.java-diff-utils</groupId>
<artifactId>java-diff-utils</artifactId>
<version>4.12</version>
</dependency>
Code-Sprache: HTML, XML (xml)
Für Gradle-Projekte:
implementation 'io.github.java-diff-utils:java-diff-utils:4.12'
Code-Sprache: JavaScript (javascript)
Die Version kann je nach Veröffentlichungsstand abweichen. Eine aktuelle Version ist auf Maven Central zu finden.
Grundprinzip: DiffUtils
Das Herzstück der Bibliothek ist die Klasse DiffUtils
. Diese ermöglicht den Vergleich von zwei Textlisten (List<String>
) und liefert eine Liste von sogenannten „Deltas“, die die Änderungen beschreiben.
Beispiel: Zwei Texte vergleichen
import java.util.*;
import com.github.difflib.DiffUtils;
import com.github.difflib.patch.*;
public class DiffExample {
public static void main(String[] args) {
List<String> original = Arrays.asList("Zeile 1", "Zeile 2", "Zeile 3");
List<String> geändert = Arrays.asList("Zeile 1", "Zeile Zwei", "Zeile 3", "Zeile 4");
Patch<String> patch = DiffUtils.diff(original, geändert);
for (AbstractDelta<String> delta : patch.getDeltas()) {
System.out.println(delta);
}
}
}
Code-Sprache: JavaScript (javascript)
Ausgabe:
ChangeDelta, position: 1, lines: [Zeile 2] to [Zeile Zwei]
InsertDelta, position: 3, lines: [] to [Zeile 4]
Code-Sprache: CSS (css)
Die Ausgabe zeigt:
- Bei Zeile 2 wurde „Zeile 2“ zu „Zeile Zwei“ geändert.
- Eine neue Zeile („Zeile 4“) wurde am Ende eingefügt.
Delta-Typen
Die Bibliothek kennt mehrere Typen von Änderungen:
- InsertDelta – Eine oder mehrere Zeilen wurden hinzugefügt.
- DeleteDelta – Zeilen wurden entfernt.
- ChangeDelta – Zeilen wurden ersetzt.
Diese Deltas enthalten Informationen über die Position und die betroffenen Zeilen. Damit lassen sich präzise Änderungen rekonstruieren oder auch rückgängig machen.
Darstellung im Unified-Diff-Format
Java-Diff-Utils kann die Unterschiede im bekannten Unified-Diff-Format (wie bei Git oder Unix-Diff) darstellen:
List<String> unifiedDiff = UnifiedDiffUtils.generateUnifiedDiff(
"original.txt", "geändert.txt", original, patch, 3);
for (String line : unifiedDiff) {
System.out.println(line);
}
Code-Sprache: JavaScript (javascript)
Beispielausgabe:
--- original.txt
+++ geändert.txt
@@ -1,3 +1,4 @@
Zeile 1
-Zeile 2
+Zeile Zwei
Zeile 3
+Zeile 4
Code-Sprache: CSS (css)
Dies macht die Bibliothek auch für Tools nützlich, die mit externen Diff-Engines interoperieren müssen.
Anwenden von Patches
Man kann mit einem erzeugten Patch
auch Änderungen anwenden oder rückgängig machen. Dies ist besonders hilfreich in Synchronisationsanwendungen oder bei Merge-Operationen:
List<String> result = DiffUtils.patch(original, patch);
Code-Sprache: HTML, XML (xml)
Für den umgekehrten Fall:
List<String> revert = DiffUtils.unpatch(geändert, patch);
Code-Sprache: HTML, XML (xml)
Einsatz in Unit-Tests
Diffs sind nützlich beim Testen von Funktionen, die Textausgaben erzeugen. Anstatt zwei Strings manuell zu vergleichen, kann man mit Java-Diff-Utils eine aussagekräftige Differenz visualisieren:
if (!expected.equals(actual)) {
Patch<String> diff = DiffUtils.diff(expected, actual);
System.err.println("Unterschiede gefunden:");
System.err.println(UnifiedDiffUtils.generateUnifiedDiff("expected", "actual", expected, diff, 3));
fail("Test fehlgeschlagen. Unterschiede siehe oben.");
}
Code-Sprache: JavaScript (javascript)
Dadurch werden Fehlerursachen schneller erkennbar.
Erweiterte Funktionen
Neben den Basisfunktionen bietet Java-Diff-Utils auch:
- Optionale Konfigurationen für die Vergleichslogik (z. B.
DiffAlgorithmFactory
) - Inline-Diffs, bei denen Änderungen innerhalb einer Zeile markiert werden
- LineComparator für benutzerdefinierte Zeilenvergleiche (z. B. ignorieren von Leerzeichen oder Groß-/Kleinschreibung)
Beispiel: Vergleich mit ignorierter Groß-/Kleinschreibung
Patch<String> patch = DiffUtils.diff(original, geändert,
DiffAlgorithmFactory.createDefault(), true);
Code-Sprache: JavaScript (javascript)
Leistung und Grenzen
Die Bibliothek ist ausreichend performant für typische Anwendungsfälle mit einigen Tausend Zeilen. Für sehr große Dateien oder Streams sollte man allerdings eine angepasste Lösung verwenden, da alles im Speicher verarbeitet wird. Für strukturierte Daten (wie XML oder JSON) ist ggf. ein spezialisierter Diff-Algorithmus sinnvoller.
Fazit
Java-Diff-Utils ist eine flexible und leicht einsetzbare Bibliothek für die Analyse und Darstellung von Unterschieden zwischen Textdateien. Ihre Stärken liegen in der einfachen API, der Unterstützung gängiger Formate wie Unified-Diff und der Möglichkeit, Patches zu erzeugen und anzuwenden.
Ob in Versionskontrollsystemen, Editoren, Testframeworks oder Synchronisierungsdiensten – überall dort, wo Unterschiede zwischen Texten eine Rolle spielen, bietet Java-Diff-Utils eine solide Basis. Die Bibliothek ist aktiv gepflegt und erfreut sich in der Java-Community großer Beliebtheit.