Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sorting files 'numerically' instead of alphabetically in java

Tags:

java

I have a folder with files which are named after timestamps.

When I try to go through each file it sorts them alphabetically and gives me this order:

/home/user/buffereddata/1
/home/user/buffereddata/100
/home/user/buffereddata/1000
/home/user/buffereddata/200
/home/user/buffereddata/2000
/home/user/buffereddata/300

But I want them sorted like this:

/home/user/buffereddata/1
/home/user/buffereddata/100
/home/user/buffereddata/200
/home/user/buffereddata/300
/home/user/buffereddata/1000
/home/user/buffereddata/2000

This is my code:

File file = new File(System.getProperty("user.home") + "/buffereddata");

if(file.exists()) {
  File[] fileArray = file.listFiles();
  Arrays.sort(fileArray);
  for(File f : fileArray) {
    System.out.println(f);
  }
}

Is there some (preferably simple) way to loop through the files in the way that I want to loop through them?

like image 202
JREN Avatar asked Jun 27 '13 09:06

JREN


People also ask

How do you sort data alphabetically in Java?

Using the toCharArray() method Get the required string. Convert the given string to a character array using the toCharArray() method. Sort the obtained array using the sort() method of the Arrays class. Convert the sorted array to String by passing it to the constructor of the String array.

What is sorting order in Java?

In Java, the collections framework provides a static method sort() that can be used to sort elements in a collection. The sort() method of the collections framework uses the merge sort algorithm to sort elements of a collection. The merge sort algorithm is based on divide and conquers rule.


3 Answers

Arrays.sort(fileArray, new Comparator<File>() {
    public int compare(File f1, File f2) {
        try {
            int i1 = Integer.parseInt(f1.getName());
            int i2 = Integer.parseInt(f2.getName());
            return i1 - i2;
        } catch(NumberFormatException e) {
            throw new AssertionError(e);
        }
    }
});
like image 68
rlegendi Avatar answered Oct 02 '22 01:10

rlegendi


While the other answers are correct in your specific case (where all file names in a given directory are numeric only), here's a solution that can compare mixed numeric / non-numeric file names, e.g. version-1.10.3.txt in an intuitive way, similar to the way Windows Explorer does it:

enter image description here

The idea (which I have blogged about here. The idea was inspired by this answer here.) is to split a file name into numeric / non-numeric segments and then compare each individual segment from the two file names either numerically (if both are numeric), or alpha-numerically otherwise:

public final class FilenameComparator implements Comparator<String> {
    private static final Pattern NUMBERS = 
        Pattern.compile("(?<=\\D)(?=\\d)|(?<=\\d)(?=\\D)");
    @Override public final int compare(String o1, String o2) {
        // Optional "NULLS LAST" semantics:
        if (o1 == null || o2 == null)
            return o1 == null ? o2 == null ? 0 : -1 : 1;

        // Splitting both input strings by the above patterns
        String[] split1 = NUMBERS.split(o1);
        String[] split2 = NUMBERS.split(o2);
        for (int i = 0; i < Math.min(split1.length, split2.length); i++) {
            char c1 = split1[i].charAt(0);
            char c2 = split2[i].charAt(0);
            int cmp = 0;

            // If both segments start with a digit, sort them numerically using 
            // BigInteger to stay safe
            if (c1 >= '0' && c1 <= '9' && c2 >= 0 && c2 <= '9')
                cmp = new BigInteger(split1[i]).compareTo(new BigInteger(split2[i]));

            // If we haven't sorted numerically before, or if numeric sorting yielded 
            // equality (e.g 007 and 7) then sort lexicographically
            if (cmp == 0)
                cmp = split1[i].compareTo(split2[i]);

            // Abort once some prefix has unequal ordering
            if (cmp != 0)
                return cmp;
        }

        // If we reach this, then both strings have equally ordered prefixes, but 
        // maybe one string is longer than the other (i.e. has more segments)
        return split1.length - split2.length;
    }
}

You can then use the comparator as such:

Arrays.sort(fileArray, Comparators.comparing(File::getName, new FilenameComparator()));
like image 25
Lukas Eder Avatar answered Oct 02 '22 02:10

Lukas Eder


you need a custom comparator

    Arrays.sort(fileArray, new Comparator<File>() {
        public int compare(File f1, File f2) {
            int n1 = Integer.parseInt(f1.getName());
            int n2 = Integer.parseInt(f1.getName());
            return Integer.compare(n1, n2);
        }});
like image 23
Evgeniy Dorofeev Avatar answered Oct 02 '22 03:10

Evgeniy Dorofeev