272

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());
    } });
viam0Zah
  • 25,949
  • 8
  • 77
  • 100
cwick
  • 26,132
  • 12
  • 37
  • 40
  • 1
    what's with the "new long" part of this? why don't you just compare the longs themselves? that would avoid you creating tons of longs just to get to the compareTo method... – John Gardner Oct 14 '08 at 23:42
  • This code don't compiles. compare methods expect that the return is a int instead of a Long. – marcospereira Oct 15 '08 at 03:40
  • I chose this form because it is less verbose ; it's a choice between a one-liner and a 6-liner. You're right that new'ing up all these Longs could be an issue. What about using Long.valueOf, so Java at least has a chance to cache frequent values? – cwick Oct 15 '08 at 15:46
  • Thanks this helped. http://stackoverflow.com/questions/7469643/how-to-sort-alphabetically-while-ignoring-case-sensitive – Jared Burrows Jun 16 '12 at 21:10
  • 2
    **Am I the only one that considers this solution insane?** You are calling `file.lastModified()` a huge amount of times. Better get all dates first and order later, so that `file.lastModified()` is only called once per file. – cprcrack Aug 07 '14 at 12:05
  • return (int) (file0.lastModified() - file1.lastModified()); much cleaner – Eric Cochran Aug 23 '14 at 23:28
  • My above comment will, of course, fail for large longs. So, not the best idea. never mind :). – Eric Cochran Aug 24 '14 at 03:45
  • 1
    You can use apache commons comparator: `Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);` – jlunavtgrad Jan 19 '17 at 20:15
  • 6
    There is a better solution with Java 8 (see viniciussss answer) : `Arrays.sort(files, Comparator.comparingLong(File::lastModified));` – starbroken Jan 15 '18 at 13:57
  • Java 8 style: Files.list(path).sorted((p1, p2) -> Long.signum(p1.toFile().lastModified() - p2.toFile().lastModified())).forEach(p -> System.out.println(p)); – pakman Feb 02 '18 at 23:58
  • using lambda `Arrays.sort(files, (File f1, File f2) -> Long.valueOf(f1.lastModified()).compareTo(f2.lastModified())); ` – Vo Thanh Tung Jan 17 '20 at 06:33

21 Answers21

108

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

Yishai
  • 90,445
  • 31
  • 189
  • 263
Dan Dyer
  • 53,737
  • 19
  • 129
  • 165
  • How do I fix the formatting here? Looks fine in the preview but the 4th link is screwed. – Dan Dyer Oct 14 '08 at 22:17
  • 1
    File.lastModified might change while sorting end result in a Comparison Method Violation Error, see: http://stackoverflow.com/questions/20431031 See http://stackoverflow.com/a/4248059/314089 for a possible better solution. – icyerasor Jan 28 '15 at 19:05
83

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());
viniciussss
  • 4,404
  • 2
  • 25
  • 42
  • 4
    This truly is the easiest solution. For lists: `files.sort(Comparator.comparingLong(File::lastModified));` – starbroken Jan 15 '18 at 13:54
  • 1
    @starbroken Your solution does not work if files is a simple array, like File[], that is returned by directory.listFiles(). – viniciussss Jan 15 '18 at 21:35
  • 1
    @starbroken For your solution to work, one needs to use `ArrayList files = new ArrayList(Arrays.asList(directory.listFiles()))` , that is not easier than just `File[] files = directory.listFiles()`. – viniciussss Jan 15 '18 at 21:43
  • Yes, I agree with you. If you have an array of files, there is no reason to create a list. (If anybody wonders, that 'additional' `ArrayList(...)` in viniciussss comment is needed to get a mutable list which can be sorted.) I found this thread looking for a way to sort a list of files. So I just added that code so people can simply copy it if they happen to have lists as well. – starbroken Jan 17 '18 at 07:17
  • The `Comparator` class doesn't have any method call `comparingLong` – zeleven Jan 28 '18 at 05:11
  • @viniciussss I use Java 8 in Android Studio, but I can't find the method. – zeleven Jan 29 '18 at 07:24
  • You can find at: https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#comparingLong-java.util.function.ToLongFunction- – viniciussss Mar 23 '19 at 06:00
  • unfortunatelly it doesnt work in Android API < 24 , but the original solution posted by the OP works! – thahgr Sep 23 '21 at 14:01
  • 10+ reputation by me...great solution – Ast Feb 24 '22 at 10:41
51

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;
Jason Orendorff
  • 42,793
  • 6
  • 62
  • 96
  • 6
    Best answer, as it is probably the only one preventing a "Comparison Method Violation Error" if the lastModified changes while sorting? – icyerasor Jan 27 '15 at 21:17
  • 2
    This should also be used when you're concerned with not getting IllegalArgumentException due to comparison method violation. Method using Map will fail if there is more than one file with the same lastModified value which would result in omitting these files. This definitely should be an accepted answer. – Android developer May 10 '17 at 07:21
38

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());
    }
});
PhannGor
  • 541
  • 4
  • 5
26

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.

ligi
  • 39,001
  • 44
  • 144
  • 244
user17163
  • 761
  • 5
  • 4
  • 5
    There is a strange error in javadoc with this solution, because javadoc says to use "LastModifiedFileComparator.LASTMODIFIED_COMPARATOR.sort(list);" to sort a list, but LASTMODIFIED_COMPARATOR is declared as "Comparator", so it does not expose any "sort" method. – Tristan Aug 03 '12 at 14:53
  • 4
    Use it like this : [link](http://www.avajava.com/tutorials/lessons/how-do-i-sort-an-array-of-files-according-to-their-last-modified-dates.html) – cleroo Aug 16 '12 at 07:48
  • 1
    File.lastModified might change while sorting end result in a Comparison Method Violation Error, see: http://stackoverflow.com/questions/20431031 See http://stackoverflow.com/a/4248059/314089 for a possible better solution. – icyerasor Jan 28 '15 at 19:06
  • 1
    love apache commons, that saved a lot of time, – redDevil Oct 09 '15 at 18:36
18

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));
Matthew Madson
  • 1,643
  • 13
  • 24
16

In Java 8:

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

hasen
  • 161,647
  • 65
  • 194
  • 231
14

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

    }
  • It isn't instant clear from where is LastModifiedFileComparator.LASTMODIFIED_COMPARATOR taken. Maybe adding link to [apache commons io](http://commons.apache.org/proper/commons-io/javadocs/api-release/index.html?org/apache/commons/io/comparator/package-summary.html) would help. – broadband Jun 10 '15 at 14:03
  • Done, Thanks broadband – Balaji Boggaram Ramanarayan Mar 29 '16 at 20:54
4

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()) 
    }
}
3
public String[] getDirectoryList(String path) {
    String[] dirListing = null;
    File dir = new File(path);
    dirListing = dir.list();

    Arrays.sort(dirListing, 0, dirListing.length);
    return dirListing;
}
  • 1
    This doesn't actually sort on the date modified property that was mentioned in the question. The sort function will use the natural ordering of the File object which is the [system-dependent lexicographic on path name](http://docs.oracle.com/javase/tutorial/collections/interfaces/order.html). –  May 24 '13 at 21:30
2
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

Anand Savjani
  • 2,484
  • 3
  • 26
  • 39
2

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()));
shubham chouhan
  • 580
  • 7
  • 8
1

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);
Vitalii Fedorenko
  • 110,878
  • 29
  • 149
  • 111
1

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 + "");
        }
Vikas
  • 11
  • 1
1
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;
}
Jaydev
  • 1,794
  • 20
  • 37
1

Using Java 8+ Here files are sorted in descending order of modified date(newer file first printed)

public void deleteOldFiles(String directory) {
    try {
        File file = new File(directory);
        Arrays.stream(file.listFiles()).filter(File::isFile)
                .sorted((file1, file2) -> {
                    if(file1.lastModified()>file2.lastModified()){
                        return -1;
                    }else if(file1.lastModified()<file2.lastModified()){
                        return 1;
                    }else {
                        return 0;
                    }
                }).forEach(System.out::println);

    } catch (Exception e) {
        System.out.println("No such directory exists: " + directory);
    }
}
0

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

Hirdesh Vishwdewa
  • 2,334
  • 1
  • 19
  • 33
0

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)

user4378029
  • 671
  • 7
  • 7
0

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!

Ward
  • 2,799
  • 20
  • 26
0

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

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.

user4378029
  • 671
  • 7
  • 7