Java 字符串数字比较器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7270447/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 19:21:38  来源:igfitidea点击:

Java String Number Comparator

javanatural-sort

提问by Jason

I have a method returning a list of String that need to be sorted. However, I'm running into the old String number sorting issue and was wondering if any one could assist with a Comparator implementation or point me in the direction of one.

我有一个方法返回需要排序的字符串列表。但是,我遇到了旧的字符串数字排序问题,想知道是否有人可以协助 Comparator 实现或为我指明方向。

The list is going to return something list this:

该列表将返回一些列表:

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
...

So, first I need to do a basic String sort, but then I need to sort by the number. The number to sort on should always trail, and may be 2 or 3 digits.

所以,首先我需要做一个基本的字符串排序,然后我需要按数字排序。要排序的数字应始终为尾数,可能是 2 或 3 位数字。

(Edit) My initial thought is to split the string on space, run StringUtils.isNumeric on the number portion, then sort. However, it seems a bit of a kludge to me.

(编辑)我最初的想法是在空间上拆分字符串,在数字部分运行 StringUtils.isNumeric,然后排序。然而,这对我来说似乎有点混乱。

Can anyone assist?

任何人都可以提供帮助吗?

采纳答案by John Kugelman

There is an article about thison Coding Horror. This is called natural sorting, where you effectively treat a group of digits as a single "character". See this questionfor some Java implementations of the idea.

Coding Horror 上有一篇关于此的文章。这称为自然排序,您可以有效地将一组数字视为单个“字符”。有关想法的一些 Java 实现,请参阅此问题

Sorting for Humans : Natural Sort Order

The default sort functions in almost every programming language are poorly suited for human consumption. What do I mean by that? Well, consider the difference between sorting filenames in Windows explorer, and sorting those very same filenames via Array.Sort()code:

Windows ExplorerArray.sort()

continued...

人类排序:自然排序

几乎所有编程语言中的默认排序函数都不太适合人类使用。我是什么意思?好吧,考虑一下在 Windows 资源管理器中对文件名进行排序与​​通过Array.Sort()代码对那些完全相同的文件名进行排序之间的区别:

Windows资源管理器数组排序()

继续...

回答by Jurjen Stellingwerff

I wrote a variation on String.CompareTo that compares the length of numbers found in the two strings. When encounting two numbers of the same length the alphanumeric compare is resumed as normal. It also skips leading zeros.

我在 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++;
    }
}

回答by Mark Peters

One way would be to use a simple regex to parse out the fields of interest in your comparator and then compare them manually. Here's an untested example:

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

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?
}

回答by Petar Minchev

You have two options. The first one is to create a class having two fields - the name and the number. Of course first parse the name and numbers. Then in the comparator first compare the name and then the number. The second one is to do the parsing at place in the comparemethod. Choose which one is more appropriate to you.

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

回答by Panayotis

Have a look at this implementation:

看看这个实现:

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

It should be fast, without any regular expressions or array manipulation, just a couple of flags and a lot of cases.

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

This should sort any combination of numbers inside strings and properly support numbers which are equal and move on.

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

回答by Maurício Linhares

A simple implementation would be like this one (this works with any string that ends with a number):

一个简单的实现就像这样(这适用于任何以数字结尾的字符串):

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

}

}

回答by comrad

I usually do this by prefixing zeros to the number and handle the whole entity as a string. then sort it.

我通常通过在数字前加上零并将整个实体作为字符串处理来做到这一点。然后排序。

See this:

看到这个:

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

}