Java比较器:字母数字字符串

3

我想实现自定义比较器以特定的方式对键的映射进行排序:

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

我不知道如何完全做到这一点。我只实现了针对数值的情况:

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));
      }
    });

但这显然不是完整的解决方案。有什么想法吗?

将数字和字母部分分开。先按照数字部分排序,然后再按照字母部分排序。 - undefined
1
创建一个类(例如:Version实现Comparable<Version>),该类包含一个int类型的变量(例如:12)和一个char或String类型的变量(例如:'a')。在这个类中实现equals、hashCode和compareTo方法。添加一个静态方法parse(String),将一个String解析成一个Version对象。然后使用TreeMap<Version, List<ETrack>>。 - undefined
1
可能是在可能包含数字的字符串上进行排序的重复问题。 - undefined
关于分割的问题,请参考这里:https://dev59.com/K2sy5IYBdhLWcg3w3Rvc - undefined
1
你绝对不是第一个提出这个问题的人。你有没有搜索解决方案? - undefined
4个回答

6

一种方法是将字符串映射为对象,并让这些对象实现Comparable

您也可以采用更简单的方法。如果您喜欢正则表达式,即使它不太高效,您也可以使用此技巧:

// 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;
}

如何使用:

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

4
这里是一行代码:
aaa = new TreeMap<String, ArrayList<ETrack>>(Comparators
    .comparingInt(s -> Integer.parseInt(s.replaceAll("\\D", "")))
    .thenComparing(s -> s.replaceAll("\\d", "")));

顺便提一下,\D 匹配非数字字符,\d 匹配数字。

无论数字和非数字部分的顺序如何,这都可以正常工作。


1
@bohemian的回答很棒,我只会做出以下更改,以便它可以在几乎任何地方使用。检查数字字符串的原因是因为如果输入字符串中没有任何数字,你将会得到一个错误。
private  Comparator stringComparator = Comparator
            .comparingInt(s -> StringUtils.isNumeric(s.toString())?Integer.parseInt(s.toString().replaceAll("\\D", "")):0)
            .thenComparing(s -> s.toString().replaceAll("\\d", ""));

请注意,StringUtils来自于org.apache.commons.lang3.StringUtils。

0
如果你只有一个字符是字母而且只有一个,你可以尝试像这样的东西:
  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;

  }
});

希望能帮到你 :) 可能有更优雅的解决方案,但是我相信如果我理解你面临的问题的话,这个方法对你来说应该是有效的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接