Reputation: 639
I know there are a lot of questions with this kind of exception, and I did found the solution, but my problem is that the same code in different project doesn't throw an exception while this one does. Both projects have the same version of Java and other libraries.
Basically I have this small function that retrieves list of files from directory, sorts them by timestamp, and then returns list of absolute file names:
public static List<String> getFiles(String dir) {
List<String> fileList = new ArrayList<String>();
File[] files = new File(dir).listFiles();
// Sort files by the date of their creation (last modification)
Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
for (File f : files) {
fileList.add(f.getAbsolutePath());
}
return fileList;
}
Basically, in one of the projects this code executes as expected, while in other project it throws IllegalArgumentException: Comparison method violates its general contract!
I know that TimSort
is default sort in Java since 1.7, and that one of the solutions is to use a property which forces usage of legacy MergeSort
. I didn't go that route... Instead I "cached" files and their timestamps as sugested here:
public static List<String> getFiles(String dir) {
List<String> fileList = new ArrayList<String>();
File[] files = new File(dir).listFiles();
FileLastModifiedPair[] pairs = new FileLastModifiedPair[files.length];
for (int i = 0; i < files.length; i++) {
pairs[i] = new FileLastModifiedPair(files[i]);
}
// Sort files by the date of their creation (last modification)
Arrays.sort(pairs);
// Take the sorted pairs and extract only the file part, discarding the timestamp
for (FileLastModifiedPair pair : pairs) {
fileList.add(pair.f.getAbsolutePath());
}
return fileList;
}
Now, there are multithreading concerns so let me explain what my code does: I have a task scheduler which, with fixed delay, calls method getFiles(String)
, and then handles each file:
private Thread handleFiles () {
return new Thread() {
public void run() {
List<String> files = getFiles("/home/user/files/");
if (files.isEmpty()) {
return;
}
for (String file : files) {
try {
// handle file...
} catch (Exception e) {
// log error...
} finally {
// delete file...
}
}
}
};
}
And when the app boots this code is called:
Date startOfTomorrow = DateTime.now()
.withTimeAtStartOfDay()
.plusDays(1)
.toDate();
scheduler.scheduleWithFixedDelay(
handleFiles(),
startOfTomorrow,
DELAY_IN_MILLIS);
Basically this is the way both of my projects handle files. My question is: why does the first getFiles(String)
method work in one project and not the other? If they used different versions of Java or other libraries (like Apache commons-io) I'd understand, but they use same versions.
EDIT #1: FileLastModifierPair.java:
public class FileLastModifiedPair implements Comparable<FileLastModifiedPair> {
public File f;
public long t;
public FileLastModifiedPair(File file) {
f = file;
t = file.lastModified();
}
public int compareTo(FileLastModifiedPair that) {
long result = this.t - that.t;
if (result < 0) {
return -1;
} else if (result > 0) {
return 1;
} else {
return 0;
}
}
}
Upvotes: 5
Views: 948
Reputation: 145
I guess in one project some of the files are modified while they are sorted. This would certainly mess with the sorting algorithm and would explain the error you get. See also Java error: Comparison method violates its general contract
Upvotes: 2
Reputation: 100249
It's likely that in one case the file modification time of some files changes during the sorting, so the sorting position changes. One day this might occur in another project as well. An approach to make a snapshot of the directory by caching these times looks correct to me.
Upvotes: 3