やりたいこと
あるディレクトリの中に内容の重複したファイルが大量に有る時、1つだけ残して重複したものを削除します。
やり方
ファイル全体を比較すると処理コストが高くて大変です。
そこで今回はファイルのハッシュ(ダイジェスト)を比較することとし、以下の方針で実装しました。
- ファイルのハッシュを取ってダイジェストを取得
- ダイジェストが
Set
に既存かチェック - 存在していれば削除
- 存在していなければ
Set
にadd
サンプルコード
とりあえず動くサンプルは以下の通りです。
import java.io.File
import java.security.MessageDigest
val sha256 : MessageDigest = MessageDigest.getInstance("SHA-256")
fun getDigest(bytes: ByteArray): List<Byte> = sha256.digest(bytes).asList()
fun getFiles(pathToDir: String): List<File> = File(pathToDir).listFiles()?.asList() ?: emptyList()
fun main() {
val files = getFiles(/* 処理対象ディレクトリのパス */)
val set = HashSet<List<Byte>>()
var count = 0
files.forEach {
val digest = getDigest(it.readBytes())
if (!set.add(digest)) {
if (it.delete()) {
println("Deleted:\t${it.name}")
count++
} else {
println("Fail delete:\t${it.name}")
}
}
}
println("\n\n$count deleted.")
}
実行結果
折り畳み
Deleted: 43_3のコピー.gif
Deleted: 46_3のコピー2.gif
Deleted: 70_1のコピー2.gif
Deleted: 94_1のコピー.gif
Deleted: 50_3のコピー2.gif
Deleted: 66_1のコピー.gif
Deleted: 95_1のコピー.jpg
Deleted: 58_3のコピー.gif
Deleted: 63_1のコピー.gif
Deleted: 32_1のコピー.jpg
Deleted: 55_3のコピー.gif
Deleted: 62_3のコピー.gif
Deleted: 49_3のコピー.gif
Deleted: 9_1のコピー2.gif
Deleted: 47_3のコピー.gif
Deleted: 96_1のコピー.jpg
Deleted: 71_1のコピー.gif
Deleted: 52_3のコピー2.gif
Deleted: 64_1のコピー2.gif
Deleted: 61_3のコピー.gif
Deleted: 56_3のコピー.gif
Deleted: 60_3のコピー2.gif
Deleted: 31_1のコピー.jpg
Deleted: 57_3のコピー2.gif
Deleted: 98_1のコピー2.jpg
Deleted: 34_1のコピー.jpg
Deleted: 68_1のコピー.gif
Deleted: 53_3のコピー.gif
Deleted: 42_3のコピー.gif
Deleted: 74_1のコピー2.gif
Deleted: 30_1のコピー.gif
Deleted: 36_1のコピー2.gif
Deleted: 65_1のコピー.gif
Deleted: 100_1のコピー.jpg
Deleted: 37_1のコピー.gif
Deleted: 35_1のコピー2.gif
Deleted: 45_3のコピー.gif
Deleted: 99_1のコピー.jpg
Deleted: 87_1のコピー2.jpg
Deleted: 33_1のコピー.jpg
Deleted: 73_1のコピー.gif
Deleted: 1_7のコピー.jpg
Deleted: 48_3のコピー.gif
Deleted: 54_3のコピー2.gif
Deleted: 51_3のコピー.gif
Deleted: 67_1のコピー.gif
Deleted: 93_1のコピー2.gif
Deleted: 44_3のコピー2.gif
Deleted: 72_1のコピー2.gif
Deleted: 97_1のコピー2.jpg
50 deleted.
解説
ハッシュの取り方
java.security.MessageDigest
を利用しました。
これは特にライブラリ等を導入せずJava
標準で使えます。
今回はテキトーにSHA-256
を指定しましたが、「極限まで重複を回避したいぜ!」という場合はSHA-512
を指定してあげればいいと思います。
重複管理
恐らくHashSet
を使うのが一番簡単かつコストも低いと思います。
また、ByteArray
ではequals
周りに不安があるため、ここではList
に変換して取り扱っています。