Java Comparator alphanumeric strings

纵然是瞬间 提交于 2020-12-06 15:32:35

问题


I want to implement my custom Comparator to sort a map of keys in this particular way:

1
2
3
4
4a
4b
5
6
11
12
12a
...

And I dont know how can do it exactly. I have impelemented this only for numeric values:

aaa = new TreeMap<String, ArrayList<ETrack>>(
    new Comparator<String>()
    {
      @Override
      public int compare(String s1, String s2)
      {
        return Integer.valueOf(s1).compareTo(Integer.valueOf(s2));
      }
    });

But this is not obiouslly the complete solution. Any idea?


回答1:


One approach is to map the strings to objects and let those objects implement Comparable.

You can do it simpler. If you like regular expressions, you can use this trick, even though it is not too efficient:

// Make sure not to compile the pattern within the method, to spare us some time.
Pattern p = Pattern.compile("^([0-9]+)([a-z]?)$");

/**
 * Converts a string to a sortable integer, ensuring proper ordering:
 * "1" becomes 256
 * "1a" becomes 353
 * "1b" becomes 354
 * "2" becomes 512
 * "100" becomes 25600
 * "100a" becomes 25697
 */
int getSortOrder(String s) {
  Matcher m = p.matcher(s);
  if(!m.matches()) return 0;
  int major = Integer.parseInt(m.group(1));
  int minor = m.group(2).isEmpty() ? 0 : m.group(2).charAt(0);
  return (major << 8) | minor;
}

Use it like this:

new Comparator<String>() {
  @Override
  public int compare(String s1, String s2) {
    return getSortOrder(s1) - getSortOrder(s2);
  }
}



回答2:


Here's a 1-liner:

aaa = new TreeMap<String, ArrayList<ETrack>>(Comparators
    .comparingInt(s -> Integer.parseInt(s.replaceAll("\\D", "")))
    .thenComparing(s -> s.replaceAll("\\d", "")));

FYI \D matches non-digits, \d matches digits.

Incidentally, this will work no matter the order of the numeric and non-numeric parts.




回答3:


if you have only one char that is a letter and not more, you can try somthing like this:

  aaa = new TreeMap<String, ArrayList<ETrack>>(
  new Comparator<String>()
  {
  @Override
  public int compare(String s1, String s2)
  {
     String newS1=s1;
     String newS2=s2;
     char s1Letter=0;
     char s1Letter=0;
     //checks if last char is not a digit by ascii
    if(!(s1.charAt(s1.length()-1)>=48 && s1.charAt(s1.length()-1)<=57)){
        newS1 = s1.substring(0,s1.length()-1);
        s1Letter = s1.charAt(s1.length()-1);
   }
      if(!(s2.charAt(s2.length()-1)>=48 && s2.charAt(s2.length()-1)<=57)){
        newS2 = s2.substring(0,s2.length()-1);
        s2Letter = s2.charAt(s2.length()-1);
   }
   int s1Val = Integer.parseInt(newS1);
   int s2Val = Integer.parseInt(newS2);
   if(s1Val<s2Val)
      return -1;
   else if(s1Val>s2Val)
      return 1;
   else if(s1Letter > s2Letter)
      return 1;
   else if(s1Letter < s2Letter)
      return -1;
   else
      retrurn 0;

  }
});

hope that helps :) there might be a more elegant solution but i believe this will work for you if I understand the problem you're facing




回答4:


@bohemian answer is great, I'd only make the following change so it can be used almost anywhere. The check for numeric string is put there because you'll get an error if your input string doesn't have any number in it.

private  Comparator stringComparator = Comparator
            .comparingInt(s -> StringUtils.isNumeric(s.toString())?Integer.parseInt(s.toString().replaceAll("\\D", "")):0)
            .thenComparing(s -> s.toString().replaceAll("\\d", ""));

Note that StringUtils is from org.apache.commons.lang3.StringUtils



来源:https://stackoverflow.com/questions/41085394/java-comparator-alphanumeric-strings

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!