【问题标题】:Java Comparator alphanumeric stringsJava 比较器字母数字字符串
【发布时间】:2017-04-26 09:45:17
【问题描述】:

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

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

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

【问题讨论】:

  • 拆分数字和字母部分。按数字部分排序,然后按字母部分排序。
  • 创建一个类(例如:Version 实现 Comparable),其中包含一个 int(例如:12)和一个 char 或 String(例如:'a')。在这个类中实现 equals、hashCode 和 compareTo。添加静态方法 parse(String) 将字符串解析为版本。然后使用 TreeMap.
  • 关于拆分请看这里:stackoverflow.com/questions/8270784/…
  • 你不是第一个有这个问题的人。您是否找到了解决方案?

标签: java string comparator


【解决方案1】:

一种方法是将字符串映射到对象并让这些对象实现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);
  }
}

【讨论】:

    【解决方案2】:

    如果你只有一个字符是字母而不是更多, 你可以试试这样的:

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

    希望有帮助:) 可能有更优雅的解决方案,但我相信如果我了解您面临的问题,这对您有用

    【讨论】:

      【解决方案3】:

      这是一个 1-liner:

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

      仅供参考,\D 匹配非数字,\d 匹配数字。

      顺便说一句,无论数字和非数字部分的顺序如何,这都可以工作。

      【讨论】:

        【解决方案4】:

        @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

        【讨论】:

          猜你喜欢
          • 2011-11-08
          • 2014-09-13
          • 2023-03-19
          • 1970-01-01
          • 2022-01-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多