Java 字符串数字比较器

发布于 2024-12-02 08:45:34 字数 550 浏览 2 评论 0原文

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

该列表将返回以下列表:

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,然后排序。然而,这对我来说似乎有点混乱。

有人可以帮忙吗?

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.

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.

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

Can anyone assist?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

一身仙ぐ女味 2024-12-09 08:45:34

Coding Horror 上有一篇关于此的文章。这称为自然排序,您可以在其中有效地处理一组数字作为单个“字符”。有关一些 Java 的信息,请参阅此问题该想法的实施。

人类排序:自然排序

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

Windows 资源管理器
Array.sort()

继续...

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

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 Explorer
Array.sort()

continued...

谎言月老 2024-12-09 08:45:34

我编写了 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++;
    }
}

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.

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++;
    }
}
日裸衫吸 2024-12-09 08:45:34

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

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

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?
}
凡尘雨 2024-12-09 08:45:34

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

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 compare method. Choose which one is more appropriate to you.

萌梦深 2024-12-09 08:45:34

看看这个实现:

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

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

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

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.

粉红×色少女 2024-12-09 08:45:34

我通常通过在数字前添加零来实现此目的,并将整个实体作为字符串处理。然后排序。

看看这个:

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

}

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

}
别想她 2024-12-09 08:45:34

一个简单的实现如下所示(这适用于任何以数字结尾的字符串):

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

}

}

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

}

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文