Reputation: 207
I have code which deletes all of the files in a directory except for the last n most recently modified ones. The code gets a list of File
objects from a directory, sorts them using a comparator which looks at File.lastModifedTime()
, then deletes the appropriate sublist.
When we upgraded to Java 7, the program started throwing java.lang.IllegalArgumentException: Comparison method violates its general contract!
. I suspect that this is because a file is modified (normal behavior) before sorting is completed so the comparator is returning inconsistent values as it check each file's last modified time.
My question is, how would you solve this problem and delete the right files?
One suggestion I read was to store the file and it's last modified time in a map before sorting so when comparisons are done, the last modified time is looked up from the map. However, if the file changed mid sort, the map isn't updated so wouldn't you end up deleting the wrong file?
Other idea I thought of was using Java NIO's file watch to keep a sorted list and re-sort whenever a file changes. But that seems fairly complicated.
I also thought of a brute force method of wrapping the sort in a try-catch statement and just retry the whole sort if it runs into the comparison method violation.
Lastly, I could just set the java.util.Arrays.useLegacyMergeSort
property and go back to the silently ignore ways of Java 6.
Upvotes: 2
Views: 3268
Reputation: 1733
Taken from my answer to the more generic question Best way to list files in Java, sorted by Date Modified?
private static List<Path> listFilesOldestFirst(final String directoryPath) throws IOException {
try (final Stream<Path> fileStream = Files.list(Paths.get(directoryPath))) {
return fileStream
.map(Path::toFile)
.collect(Collectors.toMap(Function.identity(), File::lastModified))
.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
// .sorted(Collections.reverseOrder(Map.Entry.comparingByValue())) // replace the previous line with this line if you would prefer files listed newest first
.map(Map.Entry::getKey)
.map(File::toPath) // remove this line if you would rather work with a List<File> instead of List<Path>
.collect(Collectors.toList());
}
}
private static List<File> listFilesOldestFirst(final String directoryPath) throws IOException {
final List<File> files = Arrays.asList(new File(directoryPath).listFiles());
final Map<File, Long> constantLastModifiedTimes = new HashMap<File,Long>();
for (final File f : files) {
constantLastModifiedTimes.put(f, f.lastModified());
}
Collections.sort(files, new Comparator<File>() {
@Override
public int compare(final File f1, final File f2) {
return constantLastModifiedTimes.get(f1).compareTo(constantLastModifiedTimes.get(f2));
}
});
return files;
}
Upvotes: 1
Reputation: 533560
My question is, how would you solve this problem and delete the right files?
I suggest you take the timestamps of all the files and cache them. Sort using these cached times. This way they will be sorted using a consistent timing.
File[] files = ...
final Map<File, Long> modified = new HashMap<File, Long>();
for(File file: files)
modified.put(file, file.lastModified());
Arrays.sort(files, /* Comparator using 'modified' Map */);
Upvotes: 0