简体   繁体   中英

How to sort a collection of files names numerically instead of alphabetically?

I am currently using SortedSet<String> fileNames = new TreeSet<String>() to sort file names, but they are not being ordered like I wanted to.

That's exactly what I do:

File folder = new File(PATH);
File[] listOfFiles = folder.listFiles();
SortedSet<String> fileNames = new TreeSet<String>;
for (int i = 0; i < listOfFiles.length; i++) 
{
     if (listOfFiles[i].isFile())
     {
        fileNames.add(listOfFiles[i].getName());
     }
}

Output:

X_1(1).PNG
X_1(2).PNG 
X_1(3).PNG 
X_10(1).PNG
X_10(2).PNG
X_10(3).PNG
X_100(1).PNG
X_100(2).PNG
X_100(3).PNG 

Desired output:

X_1(1).PNG
X_1(2).PNG
X_1(3).PNG
X_2(1).PNG
X_2(2).PNG
X_2(3).PNG

Put this Comparator parameter in the TreeSet constructor :

Comparator<String> stringComparator = (o1, o2) -> {
  int o1FirstNum = Integer.parseInt(o1.substring(o1.indexOf('_') + 1, o1.indexOf('(')));
  int o2FirstNum = Integer.parseInt(o2.substring(o2.indexOf('_') + 1, o2.indexOf('(')));

  if (o1FirstNum == o2FirstNum) {
    int o1SecondNum = Integer.parseInt(o1.substring(o1.indexOf('(') + 1, o1.indexOf(')')));
    int o2SecondNum = Integer.parseInt(o2.substring(o2.indexOf('(') + 1, o2.indexOf(')')));
    return o1SecondNum - o2SecondNum;
  }

  return o1FirstNum - o2FirstNum;
};

SortedSet<String> fileNames = new TreeSet<String>(stringComparator);

This Comparator should resolve your problem.

public class Test {

    public static void main(String... args) {
        String[] list = {"X_1(1).PNG", "X_1(2).PNG", "X_1(3).PNG", "X_10(1).PNG", "X_10(2).PNG", "X_10(3).PNG", "X_100(1).PNG", "X_100(2).PNG", "X_100(3).PNG"};

        SortedSet<String> fileNames = new TreeSet<>(Test::compare);
        fileNames.addAll(Arrays.asList(list));

        fileNames.forEach(System.out::println);
    }

    private static int compare(String first, String second) {
        int firstNumber = Integer.parseInt(first.substring(first.indexOf('_') + 1, first.indexOf('(')));
        int secondNumber = Integer.parseInt(second.substring(first.indexOf('_') + 1, second.indexOf('(')));

        int difference = firstNumber - secondNumber;

        if (difference != 0) {
            return difference;
        }

        firstNumber = Integer.parseInt(first.substring(first.indexOf('(') + 1, first.indexOf(')')));
        secondNumber = Integer.parseInt(second.substring(first.indexOf('(') + 1, second.indexOf(')')));

        return firstNumber - secondNumber;
    }

}

You are looking for a natural sort comparator, one which understands sequence of number.

You could use one of these:

The first may be used as a maven dependency, however since the Comparator does not implements Comparator<String> (but rather only Comparator ) you may as well do a pull request to do that.

The last one is probably the best as it is ASL 2.0.

By the way, you should keep using File (or Path) in the TreeSet and pass in a comparator:

Comparator<String> naturalSortComparator = ...;
TreeSet<File> files = new TreeSet<>(Comparators.comparing(File::getName, naturalSortComparator));

This one will sort File entries by name using the naturalSortComparator .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM