-1

I have a TreeMap which needs to be sorted based on keys. Which is default property of TreeMap. But in my case I am not able to figure out the Comparator. Following is my Code.

 public class Test {

    public static void main(String[] args) {

        Map<String, String> aMap = new TreeMap<String, String>(new MyComp());
        aMap.put("02_file.cql", "test");
        aMap.put("01.cql", "test");
        aMap.put("04.cql", "test");
        aMap.put("3_file.cql", "test");
        aMap.put("11_file.cql", "test");
        aMap.put("10_file.cql", "test");
        aMap.put("0_file.cql", "test");
        aMap.put("100_file.cql", "test");
        Set<Map.Entry<String,String>> set = aMap.entrySet();
        for(Map.Entry<String,String> e : set){
            System.out.println(e.getKey() + ":" + e.getValue());
        }

    }
}

class MyComp implements Comparator<String> {

    @Override
    public int compare(String str1, String str2) {
        return str1.compareTo(str2);
    }
}

The output is coming as :

01.cql:test
02_file.cql:test
04.cql:test
0_file.cql:test
100_file.cql:test
10_file.cql:test
11_file.cql:test
3_file.cql:test

Which is not my expected result. I am expecting result like:

0_file.cql:test
01.cql:test
02_file.cql:test
3_file.cql:test
04.cql:test
10_file.cql:test
11_file.cql:test
100_file.cql:test

Which is same as like what NameFileComparator.NAME_COMPARATOR

org.apache.commons.io.comparator.NameFileComparator;

Any suggestion?

Arpan Das
  • 1,015
  • 3
  • 24
  • 57

4 Answers4

4

The Comparator passed to the TreeMap constructor makes in a some way which compareTo() of String does : a lexicographical comparison.

But you don't want a lexicographical comparison.
In your expected, you want only a numeric comparison.
To achieve it, remove the no digit part of Strings, create two ints from that and compare the ints with Integer.compare(int, int).

class MyComp implements Comparator<String> {

  @Override
  public int compare(String str1, String str2) {
     String notDigit = "[^\\d]";
     int int1 = Integer.parseInt(str1.replaceAll(notDigit, ""));
     int int2 = Integer.parseInt(str2.replaceAll(notDigit, ""));
     return Integer.compare(int1, int2);
  }
}

Output by using this Comparator:

0_file.cql:test

01.cql:test

02_file.cql:test

3_file.cql:test

04.cql:test

10_file.cql:test

11_file.cql:test

100_file.cql:test

davidxxx
  • 125,838
  • 23
  • 214
  • 215
  • above does not work for when 2 keys proceeding characters say `01.cql:test` & `01.dql:test` are different but digit is the same. The resulting map will store only one key. – Simple-Solution Feb 20 '19 at 17:35
  • @Simple-Solution Agreed. In the original question, this case doesn't appear. But we could handle it with few changes. For example by adding an additional comparison when `Integer.compare(int1, int2)==0` by comparing the textual part after the digits. – davidxxx Feb 21 '19 at 19:22
1

It's because you are comparing the key as string, so it compares the first character, then the second, and so on.

It seems like what you want is to compare the integer each key starts with.

Pinyi Wang
  • 823
  • 5
  • 14
0

The comparison operation seems alright. It just compares the string values and apparently the "_" character have a low precedence than numbers. If you want your code to sort your files numerically, then you have to store your numeric part of your filenames as a seperate int-type key.

arkantos
  • 497
  • 3
  • 14
0

If you want the comparison done exactly as with apache commons-io NameFileComparator and you are not talking about a massive amount of filenames or concurrent calls to this method...just create a Comparator that delegates to the apache one.

public clas MyComp implements Comparator<String> {
    private final Comparator<File> delegate = <init NameFileComparator as needed>;

    @Override
    public int compare(String str1, String str2) {
        return delegate.compare(new File(str1), new File(str2));
    }
}

Then use that MyComp on the TreeSet constructor.

Take into consideration File is just an abstraction, it's not a real system file unless you decide to create one. So safe to use.

Adding the disclaimer regarding big amount of filenames, since this approach will generate a new File instance for each filename, which means small amount of memory overhead. Nothing to take into consideration in most of the scenarios.

albert_nil
  • 1,648
  • 7
  • 9