How to sort Files in Ascending order in Java or Android!

The Problem with Alphabetically sorting Files if their name contains a number.

Why Make this Tutorial?

I could have never imagined how difficult would it be to simply order your Files Alphabetically according to their names. I was making an app on Android Studio, for my Android phone which converts CBR file to PDF. When you extract the .cbr file, you get a collection of images. These images are sorted numerically, and sometimes they also contain names, ending with their number.

THE PROBLEM!

When I tried to put them in PDF file, ordering them alphabetically, I came across a strange problem. For example these four files, instead of ordering like this:

  1. ran1.jpg
  2. ran3.jpg
  3. ran10.jpg
  4. ran22.jpg
  5. school4.png
  6. school302.png

would be arranged like if I arrange them alphabetically in Java:

  1. ran1.jpg,
  2. ran10.jpg,
  3. ran22.jpg
  4. ran3.jpg
  5. school302.png
  6. school4.png

Well, the reason behind this is that the alphabetical ordering algorithms use ASCII comparisons to sort the names, each number has its own unique ASCII code, all these are summed together (the codes of both alphabets and numbers) and the smallest one is considered first in the alphabetical ordering and so on. This results in an undesirable ordered list which does not consider the numeric values within the string.

THE SEARCHING AND RESEARCHING!

I was amazed to see that this question was asked over and over again on Stack Overflow and no where could I come up with the perfect solution. There were so many limitations and restrictions with their solutions like for example the names should follow a certain format, the alphabetical name should be the same in all or of the same size etc.

After doing a lot of search and not accepting the limited solutions, I cam across many different ways and theories for sorting, I finally hit jackpot when I came across “The Alphanum Algorithm” by David Koelle. I had to slightly modify the code and my problem was solved.

THE SOLUTION!

In order to show you, how simple it would be to sort files, I am creating a function OrderMyFiles that would take as its input variable, a variable of type File (This would be the folder containing all the files that need to be sorted) and then return a File array containing the files in our desired alphabetical order.

File[] OrderMyFiles (File rootFolder){

   File[] listOfFiles = rootFolder.listFiles();
   Arrays.sort(listOfFiles, new AlphanumFileComparator() );
   return listOfFiles;
}

In order for this to work, we need to add the AlphanumFileComparator.java class to our project. You just need to create a java class by the name AlphanumFileComparator and then copy and past the following code in it and everything should run smoothly.

The code in red, is where I modified the code so that it sorts Files.

Here is the AlphanumFileComparator.java:

import java.io.File;
import java.util.Comparator;

/**
 * Created by Haider on 9/30/2015.
 * This is a modification of the 
 * AlphanumComparator class by David Koelle
 */

public class AlphanumFileComparator implements Comparator
{
    private final boolean isDigit(char ch)
    {
        return ch >= 48 && ch <= 57;
    }

    /** Length of string is passed in for improved efficiency (only need to calculate it once) **/

    private final String getChunk(String s, int slength, int marker)
    {
        StringBuilder chunk = new StringBuilder();
        char c = s.charAt(marker);
        chunk.append(c);
        marker++;
        if (isDigit(c))
        {
            while (marker < slength)
            {
                c = s.charAt(marker);
                if (!isDigit(c))
                    break;
                chunk.append(c);
                marker++;
            }
        } else
        {
            while (marker < slength)
            {
                c = s.charAt(marker);
                if (isDigit(c))
                    break;
                chunk.append(c);
                marker++;
            }
        }
        return chunk.toString();
    }

    public int compare(Object o1, Object o2)
    {
        if (!(o1 instanceof File) || !(o2 instanceof File))
        {
            return 0;
        }
        File f1 = (File)o1;
        File f2 = (File)o2;
        String s1 = f1.getName();
        String s2 = f2.getName();

        int thisMarker = 0;
        int thatMarker = 0;
        int s1Length = s1.length();
        int s2Length = s2.length();

        while (thisMarker < s1Length && thatMarker < s2Length)
        {
            String thisChunk = getChunk(s1, s1Length, thisMarker);
            thisMarker += thisChunk.length();

            String thatChunk = getChunk(s2, s2Length, thatMarker);
            thatMarker += thatChunk.length();

            /** If both chunks contain numeric characters, sort them numerically **/

            int result = 0;
            if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0)))
            {
                // Simple chunk comparison by length.
                int thisChunkLength = thisChunk.length();
                result = thisChunkLength - thatChunk.length();
                // If equal, the first different number counts
                if (result == 0)
                {
                    for (int i = 0; i < thisChunkLength; i++)
                    {
                        result = thisChunk.charAt(i) - thatChunk.charAt(i);
                        if (result != 0)
                        {
                            return result;
                        }
                    }
                }
            } else
            {
                result = thisChunk.compareTo(thatChunk);
            }

            if (result != 0)
                return result;
        }

        return s1Length - s2Length;
    }
}

My modification and how to make the code Universal !

In order to use this code to fulfill the your, no matter which object you are using, may it be of type File or Arraylist or even a self made class object etc… You first need to understand a small part of the AlphanumericComparator code. The one highlighted in red above.

The compare method takes in  two variables of type object, meaning it could be anything as all variables are child of “Object” class.

public int compare(Object o1, Object o2)
    {
...

Originally the AlphanumericComparator code was as follows:

public int compare(Object o1, Object o2)
    {
        if (!(o1 instanceof String) || !(o2 instanceof String))
        {
            return 0;
        }
        String s1 = (String)o1;
        String s2 = (String)o2;
...

So in order to stop being forced to provide a String for comparison, because I had a list of FIles, I had to modify it as follows:

 public int compare(Object o1, Object o2)
    {
        if (!(o1 instanceof File) || !(o2 instanceof File))
        {
            return 0;
        }
        File f1 = (File)o1;
        File f2 = (File)o2;
        String s1 = f1.getName();
        String s2 = f2.getName();
...

Now we can pass in two file objects into the compare method, then extract the name of each file using getName() method of the File class. Now we have the string containing the name of the file. Next, Pass the name Strings in s1 and s2 respectively since they have been used in the rest of the code, and leave the rest of the code the same.

Leave a Reply

Your email address will not be published.