Question

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());
    } });
Was it helpful?

Solution

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().

OTHER TIPS

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;

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

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

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.

In Java 8:

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

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

    }

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.

public String[] getDirectoryList(String path) {
    String[] dirListing = null;
    File dir = new File(path);
    dirListing = dir.list();

    Arrays.sort(dirListing, 0, dirListing.length);
    return dirListing;
}

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

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 + "");
        }
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;
}
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

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

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)

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top