编程

Java Diff Utils 指南

11 2025-09-26 18:50:00

1. 概述

在现代软件开发中,跟踪和可视化不同版本文件或内容之间的差异至关重要。无论我们构建的是版本控制系统、协作编辑器还是代码审查工具,高效地比较内容都至关重要。在 Java 中,实现此目的的一种常用方法是使用 Java Diff Utils。

本教程演示了如何使用 Java Diff Utils 库执行各种任务,包括逐行比较文本内容、生成统一的 diff、应用补丁以恢复或修改内容,以及构建并排 diff 视图。

2. 理解 Java Diff Utils 及其主要优点

Java Diff Utils 是一个轻量级且功能强大的库,用于计算文本数据之间的差异。它支持字符级和行级比较,并生成统一的差异输出。

它常用于版本控制系统,并应用补丁将一个版本的数据转换为另一个版本。此实用程序提供了各种类和方法来简化比较过程。

以下是 Java Diff Utils 的主要优势:

  • 简洁性:提供简洁直观的 API,包含静态实用方法
  • 可扩展性:轻松与 Spring Boot 服务和控制器集成
  • 跨平台:兼容任何支持 Java 的操作系统
  • 开源:根据 Apache 许可证可免费使用和修改

这些功能使 Java Diff Utils 成为 Java 应用中可靠文本比较功能的理想选择。

3. 设置 Diff Utils

在深入研究之前,我们将通过 Spring Initializr 使用 Maven 设置一个简单的 Spring Boot 应用。

虽然 Java Diff Utils 可以通过手动管理 JAR 和类路径在纯 Java 中使用,但带有 Maven 的 Spring Boot 可以通过 pom.xml 自动处理所有依赖项来简化此过程。这种方法不仅减少了设置时间,而且还确保了更好的跨环境可移植性和一致性。Maven 负责下载、管理和解析所有必需的工件。

首先,我们将 Java Diff Utils 依赖项添加到我们的 pom.xml 文件中:

<dependency>
    <groupId>io.github.java-diff-utils</groupId>
    <artifactId>java-diff-utils</artifactId>
    <version>4.12</version>
</dependency>

通过此设置,Maven 可确保在编译和运行时均可使用正确版本的 Java Diff Utils。

现在,我们可以直接使用 DiffUtilsPatchUnifiedDiffUtils 等核心类。这些类都是基于实用程序的,这意味着无需显式创建实例。这种设计允许直接集成到服务类、控制器层或独立的 Java 组件中。

4. 在 Java 中使用 Diff Utils

在本节中,我们将逐步构建一些基本组件,以了解如何在各种场景中使用 Java Diff Utils 库。我们将探讨几个核心用例,并研究如何在 Java 应用程序中使用各种策略比较文本内容的实现。我们将使用 Java Diff Utils 库创建用于比较内容、生成 diff、应用补丁以及并排查看更改的实现。

让我们进一步探索核心用例及其实现。

4.1. 比较字符串列表

让我们创建一个名为 TextComparatorUtil 的工具类,用于比较两个字符串列表,并生成一个表示它们差异的补丁。这个工具类简化了识别文本版本之间差异的过程:

class TextComparatorUtil {
    public static Patch<String> compare(List<String> original, List<String> revised) {
        return DiffUtils.diff(original, revised);
    }
}

让我们验证 TextComparatorUtil 是否正确检测并报告两个字符串列表之间的变化:

@Test
void givenDifferentLines_whenCompared_thenDetectsChanges() {
    var original = List.of("A", "B", "C");
    var revised = List.of("A", "B", "D");

    var patch = TextComparatorUtil.compare(original, revised);

    assertEquals(1, patch.getDeltas().size());
    assertEquals("C", patch.getDeltas().get(0).getSource().getLines().get(0));
    assertEquals("D", patch.getDeltas().get(0).getTarget().getLines().get(0));
}

4.2. 生成统一的差异

接下来,让我们创建一个类 UnifiedDiffGeneratorUtil,它在两个字符串列表之间生成统一的差异,以标准补丁格式表示差异:

class UnifiedDiffGeneratorUtil {
    public static List<String> generate(List<String> original, List<String> revised, String fileName) {
        var patch = DiffUtils.diff(original, revised);
        return UnifiedDiffUtils.generateUnifiedDiff(fileName, fileName + ".new", original, patch, 3);
    }
}

当我们指定原始内容和修改后的内容以及文件名时,它会生成一个适用于代码审查或版本控制系统的统一差异输出。

让我们编写一个 Junit 测试,以确保 UnifiedDiffGeneratorUtil 能够在统一差异输出中正确突出显示修改后的行:

@Test
void givenModifiedText_whenUnifiedDiffGenerated_thenContainsExpectedChanges() {
    var original = List.of("x", "y", "z");
    var revised = List.of("x", "y-modified", "z");

    var diff = UnifiedDiffGeneratorUtil.generate(original, revised, "test.txt");

    assertTrue(diff.stream().anyMatch(line -> line.contains("-y")));
    assertTrue(diff.stream().anyMatch(line -> line.contains("+y-modified")));
}

4.3. 应用补丁

接下来,我们编写一个 PatchUtil 类,生成并应用补丁来更新原始内容。它将原始字符串列表转换为修改后的版本:

class PatchUtil {
    public static List<String> apply(List<String> original, List<String> revised) throws PatchFailedException {
        var patch = DiffUtils.diff(original, revised);
        return DiffUtils.patch(original, patch);
    }
}

它首先计算差异,然后应用生成的补丁来更新原始内容。

现在,我们将验证 PatchUtil 是否正确应用了补丁,以便原始列表与修改后的列表匹配:

@Test
void givenPatch_whenApplied_thenMatchesRevised() throws PatchFailedException {
    var original = List.of("alpha", "beta", "gamma");
    var revised = List.of("alpha", "beta-updated", "gamma");

    var result = PatchUtil.apply(original, revised);

    assertEquals(revised, result);
}

4.4. 构建并排差异视图

最后,让我们创建一个 SideBySideViewUtil 类,它提供一种方法以可读的格式显示两个字符串列表之间的差异:

public class SideBySideViewUtil {
    private static final Logger logger = Logger.getLogger(SideBySideViewUtil.class.getName());

    public static void display(List<String> original, List<String> revised)
    {
        var patch = DiffUtils.diff(original, revised);
        patch.getDeltas().forEach(delta -> {
            logger.log(Level.INFO,"Change: " + delta.getType());
            logger.log(Level.INFO,"Original: " + delta.getSource().getLines());
            logger.log(Level.INFO,"Revised: " + delta.getTarget().getLines());
        });
    }
}

它识别每项更改,并打印更改类型以及原始版本和修订版本中相应的行。这使得快速直观地呈现和理解文本版本之间的修改变得更容易。

虽然 Java Diff Utils 不会直接生成 HTML 视图,但它会公开来自增量的源行和目标行。这些可用于构建自定义的可视化表示,然后将其转换为用于基于 Web 的 diff 查看器的格式化 HTML。

此测试验证在比较两个不同的字符串列表时,SideBySideViewUtil.display() 方法是否执行无误:

@Test
void givenDifferentLists_whenDisplayCalled_thenNoExceptionThrown() {
    List<String> original = List.of("line1", "line2", "line3");
    List<String> revised = List.of("line1", "line2-modified", "line3", "line4");

    SideBySideViewUtil.display(original, revised);
}

5. 小结

本文探讨了 Java Diff Utils 及其各种功能。Java Diff Utils 提供了一个灵活的开源解决方案,用于比较 Java 应用中的文本数据。从基本的逐行比较到完整的统一比较和补丁功能,它可作为构建强大的版本控制或变更跟踪系统的基础工具。

Java Diff Utils 只需极简的设置和高度可读的输出,对于使用版本化数据、协作编辑工具或文件监控系统的开发者来说,它是必不可少的工具。

与往常一样,这些示例的代码可在 GitHub 上获取:https://github.com/eugenp/tutorials/tree/master/libraries-7

 

下一篇