Best way to list files in Java, sorted by Date Modified?

JavaFileSorting

Java Problem Overview


I want to get a list of files in a directory, but I want to sort it such that the oldest files are first. My solution was to call File.listFiles and just resort the list based on File.lastModified, but I was wondering if there was a better way.

Edit: My current solution, as suggested, is to use an anonymous Comparator:

File[] files = directory.listFiles();
    
Arrays.sort(files, new Comparator<File>(){
    public int compare(File f1, File f2)
    {
        return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
    } });

Java Solutions


Solution 1 - Java

I think your solution is the only sensible way. The only way to get the list of files is to use File.listFiles() and the documentation states that this makes no guarantees about the order of the files returned. Therefore you need to write a Comparator that uses File.lastModified() and pass this, along with the array of files, to Arrays.sort().

Solution 2 - Java

Elegant solution since Java 8:

File[] files = directory.listFiles();
Arrays.sort(files, Comparator.comparingLong(File::lastModified));

Or, if you want it in descending order, just reverse it:

File[] files = directory.listFiles();
Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());

Solution 3 - Java

This might be faster if you have many files. This uses the decorate-sort-undecorate pattern so that the last-modified date of each file is fetched only once rather than every time the sort algorithm compares two files. This potentially reduces the number of I/O calls from O(n log n) to O(n).

It's more code, though, so this should only be used if you're mainly concerned with speed and it is measurably faster in practice (which I haven't checked).

class Pair implements Comparable {
    public long t;
    public File f;

    public Pair(File file) {
        f = file;
        t = file.lastModified();
    }

    public int compareTo(Object o) {
        long u = ((Pair) o).t;
        return t < u ? -1 : t == u ? 0 : 1;
    }
};

// Obtain the array of (file, timestamp) pairs.
File[] files = directory.listFiles();
Pair[] pairs = new Pair[files.length];
for (int i = 0; i < files.length; i++)
    pairs[i] = new Pair(files[i]);

// Sort them by timestamp.
Arrays.sort(pairs);

// Take the sorted pairs and extract only the file part, discarding the timestamp.
for (int i = 0; i < files.length; i++)
    files[i] = pairs[i].f;

Solution 4 - Java

What's about similar approach, but without boxing to the Long objects:

File[] files = directory.listFiles();

Arrays.sort(files, new Comparator<File>() {
    public int compare(File f1, File f2) {
        return Long.compare(f1.lastModified(), f2.lastModified());
    }
});

Solution 5 - Java

You might also look at apache commons IO, it has a built in last modified comparator and many other nice utilities for working with files.

Solution 6 - Java

In Java 8:

Arrays.sort(files, (a, b) -> Long.compare(a.lastModified(), b.lastModified()));

Solution 7 - Java

If the files you are sorting can be modified or updated at the same time the sort is being performed:


Java 8+

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());
    }
}

Java 7

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;
}


Both of these solutions create a temporary map data structure to save off a constant last modified time for each file in the directory. The reason we need to do this is that if your files are being updated or modified while your sort is being performed then your comparator will be violating the transitivity requirement of the comparator interface's general contract because the last modified times may be changing during the comparison.

If, on the other hand, you know the files will not be updated or modified during your sort, you can get away with pretty much any other answer submitted to this question, of which I'm partial to:

Java 8+ (No concurrent modifications during sort)

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)
            .sorted(Comparator.comparing(File::lastModified))
            .map(File::toPath)  // remove this line if you would rather work with a List<File> instead of List<Path>
            .collect(Collectors.toList());
    }
}

Note: I know you can avoid the translation to and from File objects in the above example by using Files::getLastModifiedTime api in the sorted stream operation, however, then you need to deal with checked IO exceptions inside your lambda which is always a pain. I'd say if performance is critical enough that the translation is unacceptable then I'd either deal with the checked IOException in the lambda by propagating it as an UncheckedIOException or I'd forego the Files api altogether and deal only with File objects:

final List<File> sorted = Arrays.asList(new File(directoryPathString).listFiles());
sorted.sort(Comparator.comparing(File::lastModified));

Solution 8 - Java

Imports :

org.apache.commons.io.comparator.LastModifiedFileComparator

Apache Commons

Code :

public static void main(String[] args) throws IOException {
		File directory = new File(".");
		// get just files, not directories
		File[] files = directory.listFiles((FileFilter) FileFileFilter.FILE);

		System.out.println("Default order");
		displayFiles(files);

		Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
		System.out.println("\nLast Modified Ascending Order (LASTMODIFIED_COMPARATOR)");
		displayFiles(files);

		Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
		System.out.println("\nLast Modified Descending Order (LASTMODIFIED_REVERSE)");
		displayFiles(files);

	}

Solution 9 - Java

public String[] getDirectoryList(String path) {
    String[] dirListing = null;
    File dir = new File(path);
    dirListing = dir.list();
    
    Arrays.sort(dirListing, 0, dirListing.length);
    return dirListing;
}

Solution 10 - Java

Here's the Kotlin way of doing it if any one is looking for it :

val filesList = directory.listFiles()

filesList?.let{ list ->
    Arrays.sort(list) { 
        f1, f2 -> f2.lastModified().compareTo(f1.lastModified()) 
    }
}

Solution 11 - Java

Collections.sort(listFiles, new Comparator<File>() {
        public int compare(File f1, File f2) {
            return Long.compare(f1.lastModified(), f2.lastModified());
        }
    });

where listFiles is the collection of all files in ArrayList

Solution 12 - Java

let array name -> files.


Ascending -> Arrays.sort(files, (o1, o2) -> Long.compare(o1.lastModified(), o2.lastModified()));

Descending -> Arrays.sort(files, (o1, o2) -> Long.compare(o2.lastModified(), o1.lastModified()));

Solution 13 - Java

You can try guava Ordering:

Function<File, Long> getLastModified = new Function<File, Long>() {
    public Long apply(File file) {
        return file.lastModified();
    }
};

List<File> orderedFiles = Ordering.natural().onResultOf(getLastModified).
                          sortedCopy(files);

Solution 14 - Java

You can use Apache LastModifiedFileComparator library

 import org.apache.commons.io.comparator.LastModifiedFileComparator;  


File[] files = directory.listFiles();
		Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
		for (File file : files) {
			Date lastMod = new Date(file.lastModified());
			System.out.println("File: " + file.getName() + ", Date: " + lastMod + "");
		}

Solution 15 - Java

private static List<File> sortByLastModified(String dirPath) {
    List<File> files = listFilesRec(dirPath);
    Collections.sort(files, new Comparator<File>() {
        public int compare(File o1, File o2) {
            return Long.compare(o1.lastModified(), o2.lastModified());
        }
    });
    return files;
}

Solution 16 - Java

I came to this post when i was searching for the same issue but in android. I don't say this is the best way to get sorted files by last modified date, but its the easiest way I found yet.

Below code may be helpful to someone-

File downloadDir = new File("mypath");    
File[] list = downloadDir.listFiles();
    for (int i = list.length-1; i >=0 ; i--) {
        //use list.getName to get the name of the file
    }

Thanks

Solution 17 - Java

There is a very easy and convenient way to handle the problem without any extra comparator. Just code the modified date into the String with the filename, sort it, and later strip it off again.

Use a String of fixed length 20, put the modified date (long) into it, and fill up with leading zeros. Then just append the filename to this string:

String modified_20_digits = ("00000000000000000000".concat(Long.toString(temp.lastModified()))).substring(Long.toString(temp.lastModified()).length()); 

result_filenames.add(modified_20_digits+temp.getAbsoluteFile().toString());

What happens is this here:

Filename1: C:\data\file1.html Last Modified:1532914451455 Last Modified 20 Digits:00000001532914451455

Filename1: C:\data\file2.html Last Modified:1532918086822 Last Modified 20 Digits:00000001532918086822

transforms filnames to:

Filename1: 00000001532914451455C:\data\file1.html

Filename2: 00000001532918086822C:\data\file2.html

You can then just sort this list.

All you need to do is to strip the 20 characters again later (in Java 8, you can strip it for the whole Array with just one line using the .replaceAll function)

Solution 18 - Java

A slightly more modernized version of the answer of @jason-orendorf.

Note: this implementation keeps the original array untouched, and returns a new array. This might or might not be desirable.

files = Arrays.stream(files)
		.map(FileWithLastModified::ofFile)
		.sorted(comparingLong(FileWithLastModified::lastModified))
		.map(FileWithLastModified::file)
		.toArray(File[]::new);

private static class FileWithLastModified {
	private final File file;
	private final long lastModified;

	private FileWithLastModified(File file, long lastModified) {
		this.file = file;
		this.lastModified = lastModified;
	}

	public static FileWithLastModified ofFile(File file) {
		return new FileWithLastModified(file, file.lastModified());
	}

	public File file() {
		return file;
	}

	public long lastModified() {
		return lastModified;
	}
}

But again, all credits to @jason-orendorf for the idea!

Solution 19 - Java

In java 6, the best way is:

  File[] listaArchivos = folder.listFiles();
            Arrays.sort(listaArchivos, new Comparator<File>() {
                @Override
                public int compare(File f1, File f2) {
                    return (f1.lastModified() < f2.lastModified()) ? -1 : ((f1.lastModified() == f2.lastModified()) ? 0 : 1);
                }
            }); 

Solution 20 - Java

There is also a completely different way which may be even easier, as we do not deal with large numbers.

Instead of sorting the whole array after you retrieved all filenames and lastModified dates, you can just insert every single filename just after you retrieved it at the right position of the list.

You can do it like this:

list.add(1, object1)
list.add(2, object3)
list.add(2, object2)

After you add object2 to position 2, it will move object3 to position 3.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncwickView Question on Stackoverflow
Solution 1 - JavaDan DyerView Answer on Stackoverflow
Solution 2 - JavaviniciussssView Answer on Stackoverflow
Solution 3 - JavaJason OrendorffView Answer on Stackoverflow
Solution 4 - JavaPhannGorView Answer on Stackoverflow
Solution 5 - Javauser17163View Answer on Stackoverflow
Solution 6 - JavahasenView Answer on Stackoverflow
Solution 7 - JavaMatthew MadsonView Answer on Stackoverflow
Solution 8 - JavaBalaji Boggaram RamanarayanView Answer on Stackoverflow
Solution 9 - JavaCalvin SchultzView Answer on Stackoverflow
Solution 10 - JavaNoviceCoderView Answer on Stackoverflow
Solution 11 - JavaAnand SavjaniView Answer on Stackoverflow
Solution 12 - Javashubham chouhanView Answer on Stackoverflow
Solution 13 - JavaVitalii FedorenkoView Answer on Stackoverflow
Solution 14 - JavaVikasView Answer on Stackoverflow
Solution 15 - JavaJaydevView Answer on Stackoverflow
Solution 16 - JavaHirdesh VishwdewaView Answer on Stackoverflow
Solution 17 - Javauser4378029View Answer on Stackoverflow
Solution 18 - JavaWardView Answer on Stackoverflow
Solution 19 - JavajosemerazoView Answer on Stackoverflow
Solution 20 - Javauser4378029View Answer on Stackoverflow