Java字符串数字比较器

9
我是一个有用的助手,可以为您翻译文本。

我有一个返回字符串列表的方法需要进行排序。然而,我遇到了旧的字符串数字排序问题,想知道是否有人能够帮助我实现Comparator或指引我实现。

该列表将返回以下内容:

State Lower Legislative District 1
State Lower Legislative District 11
State Lower Legislative District 12
...
State Lower Legislative District 2
...
State Lower Legislative District 100
...
State Upper Legislative District 1
State Upper Legislative District 11
...

首先,我需要进行基本的字符串排序,但是接下来我需要按数字排序。要排序的数字应该始终在后面,并且可能是2或3位数。

(编辑) 我最初的想法是在空格上拆分字符串,在数字部分上运行StringUtils.isNumeric,然后进行排序。但是,对我来说似乎有点笨拙。

有人可以帮忙吗?


1
可能是[Java中自然排序字符串比较 - 是否内置了一种?]的重复内容(https://dev59.com/snM_5IYBdhLWcg3wt1g0) - John Kugelman
7个回答

7

在 Coding Horror 上有一篇关于这个的文章。这被称为自然排序,其中你可以将一组数字有效地视为一个“字符”。请参见此问题以获取一些实现该想法的 Java。

人性化排序:自然排序顺序

几乎所有编程语言中的默认排序函数都不适合人类消费。我的意思是什么?嗯,考虑在 Windows 资源管理器中排序文件名与通过 Array.Sort() 代码对相同文件名进行排序之间的差异:

Windows Explorer Array.sort()

继续阅读...


6

我写了一个变种的String.CompareTo函数,它比较两个字符串中找到的数字的长度。当遇到两个长度相同时,继续进行字母数字比较。它还跳过前导零。

public static int compareNatural(String a, String b) {
    int la = a.length();
    int lb = b.length();
    int ka = 0;
    int kb = 0;
    while (true) {
        if (ka == la)
            return kb == lb ? 0 : -1;
        if (kb == lb)
            return 1;
        if (a.charAt(ka) >= '0' && a.charAt(ka) <= '9' && b.charAt(kb) >= '0' && b.charAt(kb) <= '9') {
            int na = 0;
            int nb = 0;
            while (ka < la && a.charAt(ka) == '0')
                ka++;
            while (ka + na < la && a.charAt(ka + na) >= '0' && a.charAt(ka + na) <= '9')
                na++;
            while (kb < lb && b.charAt(kb) == '0')
                kb++;
            while (kb + nb < lb && b.charAt(kb + nb) >= '0' && b.charAt(kb + nb) <= '9')
                nb++;
            if (na > nb)
                return 1;
            if (nb > na)
                return -1;
            if (ka == la)
                return kb == lb ? 0 : -1;
            if (kb == lb)
                return 1;

        }
        if (a.charAt(ka) != b.charAt(kb))
            return a.charAt(ka) - b.charAt(kb);
        ka++;
        kb++;
    }
}

2

一种方法是使用简单的正则表达式来解析比较器中感兴趣的字段,然后手动进行比较。这里是一个未经测试的示例:

private static final Pattern pattern = Pattern.compile("^State (Lower|Upper) Legislative District (\\d+)$");

public int compare(String a, String b) {
    Matcher matcher1 = pattern.matcher(a);
    Matcher matcher2 = pattern.matcher(b);
    if( matcher1.matches() && matcher2.matches() ) {
        //compare upper/lower
        int upperLowerComparison = matcher1.group(1).compareTo(matcher2.group(1));
        if ( upperLowerComparison != 0 ) {
            return upperLowerComparison;
        }

        //number comparison
        return Integer.valueOf(matcher1.group(2)).compareTo(Integer.valueOf(matcher2.group(2));
    }

    //...what to do if they don't match?
}

要找到这个数字,你可以使用 lastIndexOf 找到最后一个空格,然后使用 substring(lastIndex + 1)。我认为在这里使用正则表达式有些过头了。 - Petar Minchev
@Petar:我使用正则表达式是因为 OP 可能对示例进行了一些清理。一旦你有一个稍微复杂一点的示例,你可能需要正则表达式或完整的解析器。这是一个更通用的答案,用于展示如何提取感兴趣的字段,但正如你所说,它对于这个确切的数据也是过度的。 - Mark Peters

1
请看这个实现:
public static int naturalCompare(String a, String b, boolean ignoreCase) {
    if (ignoreCase) {
        a = a.toLowerCase();
        b = b.toLowerCase();
    }
    int aLength = a.length();
    int bLength = b.length();
    int minSize = Math.min(aLength, bLength);
    char aChar, bChar;
    boolean aNumber, bNumber;
    boolean asNumeric = false;
    int lastNumericCompare = 0;
    for (int i = 0; i < minSize; i++) {
        aChar = a.charAt(i);
        bChar = b.charAt(i);
        aNumber = aChar >= '0' && aChar <= '9';
        bNumber = bChar >= '0' && bChar <= '9';
        if (asNumeric)
            if (aNumber && bNumber) {
                if (lastNumericCompare == 0)
                    lastNumericCompare = aChar - bChar;
            } else if (aNumber)
                return 1;
            else if (bNumber)
                return -1;
            else if (lastNumericCompare == 0) {
                if (aChar != bChar)
                    return aChar - bChar;
                asNumeric = false;
            } else
                return lastNumericCompare;
        else if (aNumber && bNumber) {
            asNumeric = true;
            if (lastNumericCompare == 0)
                lastNumericCompare = aChar - bChar;
        } else if (aChar != bChar)
            return aChar - bChar;
    }
    if (asNumeric)
        if (aLength > bLength && a.charAt(bLength) >= '0' && a.charAt(bLength) <= '9') // as number
            return 1;  // a has bigger size, thus b is smaller
        else if (bLength > aLength && b.charAt(aLength) >= '0' && b.charAt(aLength) <= '9') // as number
            return -1;  // b has bigger size, thus a is smaller
        else
            return lastNumericCompare;
    else
        return aLength - bLength;
}

它应该很快,没有任何正则表达式或数组操作,只需要一些标志和很多情况。

这应该对字符串中的任何数字组合进行排序,并正确支持相等的数字并继续移动。


它说"325"等于"325_0",但实际上并不相等。 - José Roberto Araújo Júnior

1

你有两个选项。第一种是创建一个具有两个字段的类 - 名称和数字。当然,首先解析名称和数字。然后在比较器中首先比较名称,然后比较数字。第二种方法是在compare方法中进行解析。选择哪个更适合你。


0

通常我是通过在数字前添加零,并将整个实体视为字符串来完成这个操作,然后进行排序。

看一下这个例子:

public abstract class MyNumberComparator {

    protected int doCompare(final String number1, final String number2) {
       String strNumber1 = fillUpLeftWithZeros(number1, 30);
       String strNumber2 = fillUpLeftWithZeros(number2, 30);    

       return strNumber1.toUpperCase().compareTo(strNumber2.toUpperCase());    
   }

}

'fillUpLeftWithZeros'是什么?你能告诉我们它的代码吗? - pubkey

0
一个简单的实现方式如下(适用于任何以数字结尾的字符串):
public class SplitComparator implements Comparator<String> {

  static class Pair implements Comparable<Pair> {

      private String name;
      private Integer number;

      public Pair(String value) {       
        value = value.trim();
        this.name = value.substring( 0, value.lastIndexOf(" ") );
        this.number = Integer.valueOf( value.substring( value.lastIndexOf(" ") + 1, value.length() ) );
      }

      @Override
      public int compareTo( Pair right) {

        int result = this.name.compareTo( right.name );

        if ( result == 0 ) {
            result = this.number.compareTo( right.number );
        }

        return result;
      } 

  }

  @Override
  public int compare(String left, String right) {                       
    return new Pair( left ).compareTo( new Pair( right ) );
  }

  public static void main( String ... args ) {

    String[] values = { "State Lower Legislative District 1", 
            "State Lower Legislative District 11",
            "State Upper Legislative District 1",
            "State Upper Legislative District 11"};

    SplitComparator comparator = new SplitComparator();

    System.out.println( comparator.compare( values[1] , values[0]) );
    System.out.println( comparator.compare( values[0] , values[1]) );
    System.out.println( comparator.compare( values[0] , values[3]) );

}

}

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