比较两个文件有很多写法:读进内存、算摘要、自己写循环,或者 Files.mismatch。这篇用 128 MiB 的真实文件量出它们的耗时与内存分配,以及 Files.mismatch 在边界情况下的返回值。
内容相同的两个文件:-1 "hello world" vs "hello_world"(第 5 个字节不同,下标从 0 起):5 "hello world" vs "hello"(后者是前者的前缀):5(= 较短文件的长度 5) 参数顺序对调:5、5 空文件 vs 非空文件:0 同一个路径与自己比较:-1 符号链接与它指向的文件:-1 其中一个文件不存在:NoSuchFileException:<dir>/nope 文件与目录:IOException:Is a directory 空文件与 /dev/null:-1
生成 A 与 5 个变体,每个 128 MiB B_start Files.mismatch = 0 中位数 0.1 ms;需要比较的字节数 0 MiB(两个文件各一份) B_mid Files.mismatch = 67108864 中位数 20.3 ms;需要比较的字节数 64 MiB(两个文件各一份) B_end Files.mismatch = 134217727 中位数 40.9 ms;需要比较的字节数 127 MiB(两个文件各一份) B_same Files.mismatch = -1 中位数 41.2 ms;需要比较的字节数 128 MiB(两个文件各一份) B_longer Files.mismatch = 134217728 中位数 39.3 ms;需要比较的字节数 128 MiB(两个文件各一份)
[B_same]
Files.mismatch 中位数 38.8 ms;每次调用线程分配约 0.0 MiB
readAllBytes × 2 + Arrays.equals 中位数 65.3 ms;每次调用线程分配约 213.3 MiB
SHA-256 两个文件再比较摘要 中位数 138.7 ms;每次调用线程分配约 1.7 MiB
自写循环 8 KiB 缓冲 中位数 37.1 ms;每次调用线程分配约 0.0 MiB
自写循环 64 KiB 缓冲 中位数 25.5 ms;每次调用线程分配约 0.1 MiB
自写循环 1 MiB 缓冲 中位数 24.7 ms;每次调用线程分配约 1.7 MiB
[B_start]
Files.mismatch 中位数 0.0 ms;每次调用线程分配约 0.0 MiB
readAllBytes × 2 + Arrays.equals 中位数 58.4 ms;每次调用线程分配约 213.3 MiB
SHA-256 两个文件再比较摘要 中位数 133.3 ms;每次调用线程分配约 1.7 MiB
自写循环 8 KiB 缓冲 中位数 0.0 ms;每次调用线程分配约 0.0 MiB
自写循环 64 KiB 缓冲 中位数 0.1 ms;每次调用线程分配约 0.1 MiB
自写循环 1 MiB 缓冲 中位数 0.3 ms;每次调用线程分配约 1.7 MiBA 与 B_longer 的大小:134217728 / 134217729(B 只比 A 多 1 字节,前缀相同) Files.mismatch(A, B) 返回 134217728,中位数 40.1 ms;先比 Files.size 再决定要不要 mismatch(大小不同就认为不相同,返回 0 的占位):中位数 0.007 ms 结论:Files.mismatch 返回的是第一个差异的位置,所以大小不同、前缀相同的两个文件它也要读完较短的那个;只想知道“是否相同”时,先比较 Files.size 可以省掉这次读取