【问题标题】:Find the longest Path common to two Paths in Java在Java中找到两条路径共有的最长路径
【发布时间】:2019-02-08 15:38:20
【问题描述】:

如果我有两条路径,如何找到两者中最长的公共路径?

import java.nio.file.Path;
import java.nio.file.Paths;

Path common(Path pathA, Path pathB) {
    ...
}
...
common(Paths.get("/a/b/c/d/e"), Paths.get("/a/b/c/g/h"))

预期输出:

Paths.get("/a/b/c")

【问题讨论】:

  • 你想找到最长的公共前缀吗?或者任何最长的公共子路径?
  • 您可以拆分字符串并比较“/”之间的部分。最后返回正确的子字符串。
  • @MichałZiober 最长的公共前缀。它应该找到两个路径共有的最近的父目录。

标签: java file


【解决方案1】:

试试这个简单的想法

    Path a = Paths.get("a/b/c/d/e");
    Path b = Paths.get("a/b/c/g/h");

    // Normalize
    a = a.normalize();
    b = b.normalize();

    // Create common root
    Path common = null;
    if (a.isAbsolute() && b.isAbsolute() && a.getRoot().equals(b.getRoot())) {
            common = a.getRoot();
    }
    else if (!a.isAbsolute() && !b.isAbsolute()) {
            common = Paths.get("");
    }

    // Iterate from root until names differ
    if (common != null) {
            int n = Math.min(a.getNameCount(), b.getNameCount());
            for (int i=0; i<n; i++) {
                    if (a.getName(i).equals(b.getName(i))) {
                            common = common.resolve(a.getName(i));
                    }
                    else {
                            break;
                    }
            }
    }

    // Show
    System.out.println(common);

【讨论】:

  • 这看起来不错,除非你使用绝对路径 "/a/b/c" 你会得到相对路径 "a/b/c"。
  • 您可能想添加一个getRoot() 电话。
  • 感谢@DodgyCodeException,我添加了支持这两种情况的代码。
  • 现在它在 Linux 上运行良好,但在 Windows 上却不行("C:\a\b", "D:\f\g" -> "\" ?)
  • @DodgyCodeException,不错。我已经更新了答案。
【解决方案2】:
Path path1 = Paths.get("/a/b/c/d/e");
Path path2 = Paths.get("/a/b/c/g/h");

您可以将路径彼此相对化:

Path relativePath = path1.relativize(path2).normalize();
// result: ../../g/h

然后去父级直到路径以..结束

while(relativePath != null && !relativePath.endsWith("..")) {
    relativePath = relativePath.getParent();
}
// result: ../.. (but may also be null)

结果可以应用回两条路径中的任何一条:

Path result = path1.resolve(relativePath).normalize()
// result: /a/b/c

【讨论】:

    【解决方案3】:

    我们可以从最长的可能开始生成所有子路径,并检查哪两个相等:

    private Path commonPath(Path path0, Path path1) {
        if (path0.equals(path1)) {
            return path0;
        }
    
        path0 = path0.normalize();
        path1 = path1.normalize();
        int minCount = Math.min(path0.getNameCount(), path1.getNameCount());
        for (int i = minCount; i > 0; i--) {
            Path sp0 = path0.subpath(0, i);
            if (sp0.equals(path1.subpath(0, i))) {
                String root = Objects.toString(path0.getRoot(), "");
                return Paths.get(root, sp0.toString());
            }
        }
    
        return path0.getRoot();
    }
    

    及用法:

    Map<String, String> paths = new LinkedHashMap<>();
    paths.put("/a/b/c", "/a/b/d");
    paths.put("/a/", "/a/b/d");
    paths.put("/f/b/c", "/a/b/d");
    paths.put("/a/b/c/d/e", "/a/b/f/../c/g");
    paths.put("C:/Winnt/System32", "C:/Winnt/System64");
    
    paths.forEach((k, v) ->
            System.out.println(
                    k + " = " + v + " => " + commonPath(Paths.get(k), Paths.get(v))));
    

    上面的代码打印:

    /a/b/c = /a/b/d => /a/b
    /a/ = /a/b/d => /a
    /f/b/c = /a/b/d => /
    /a/b/c/d/e = /a/b/f/../c/g => /a/b/c
    C:/Winnt/System32 = C:/Winnt/System64 => C:/Winnt
    

    【讨论】:

    • 您可能需要添加一个getRoot() 调用来解决剥离初始“/”的问题。
    • 好收获。谢谢!我根据您的建议更新了答案。
    【解决方案4】:

    无论是什么问题,尝试使用流来回答总是很有趣:

    public static Path commonPath(Path a, Path b) {
        Path other = b.normalize();
        return Stream.iterate(a.normalize(), Path::getParent)
            .takeWhile(Objects::nonNull)
            .filter(parent -> Stream.iterate(other, Path::getParent).takeWhile(Objects::nonNull)
                                    .anyMatch(x -> Objects.equals(x, parent)))
            .findFirst().orElse(null);
    }
    

    这适用于 Windows / Linux,例如尝试@Michał Ziober 答案中的测试数据。这只是迭代 first 的父层次结构,直到它遇到另一个的父级。过滤器的主体可以作为新方法拉出boolean isParentOf(Path parent, Path child)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-06-01
      • 2019-06-23
      • 1970-01-01
      • 2018-10-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多