【问题标题】:Sorting array of strings that contain number对包含数字的字符串数组进行排序
【发布时间】:2020-02-18 14:43:46
【问题描述】:

我正在为我的大学实现一些代码,我必须按其名称对两个类进行排序。因此,我开始使用 Java 的 compareTo 处理字符串,但它没有正确执行。比如我有TEST-6TEST-10这两个名字。但是,结果是TEST-10 领先于TEST-6

我已经搜索并得到了这个解决方案:

private int compare(String o1, String o2) {
    return extractInt(o1) - extractInt(o2);
}
private int extractInt(String s) {
    String num = s.replaceAll("\\D", "");
    // return 0 if no digits found
    return num.isEmpty() ? 0 : Integer.parseInt(num);
}

但我的字符串可以采用任何形式。当我尝试这个测试时:TEST-6TEST10) 结果是TEST-6 领先于TEST10,但我期望的是TEST10 然后是TEST-6

预期的结果应该是正常的字符串比较,但是在需要的时候比较全数。因此,如果数字之前的子字符串相等,则比较该数字,如果不相等,则继续进行字符串比较。 或者是这样的:

TE
TES-100
TEST-1
TEST-6
TESTT-0
TEXT-2
109

【问题讨论】:

  • 它总是一个非数字后跟一个正整数吗?可以有a1b2c3d4之类的东西吗?
  • 另外,为什么109 是最后一个?既然非数字部分是空字符串,不应该是第一个吗?
  • 您基本上必须将字符串拆分为数字和非数字部分并一一比较。但是,必须解析数字部分才能获得正确的顺序。
  • -6 小于 10,因此排序按预期工作。
  • 您的 compareTo 可以比较 数字之前的两个字符串(如果字符串没有任何数字,则需要整个字符串进行比较)。所以:a= "foo1bar"b="foo2" 将导致 "foo""foo" 进行比较。如果两个字符串现在已经排序,那么很好,但是当它们相等时(如"foo=foo"),那么你比较它们的数字1 comparedTo 2——这将导致foo1barfoo2之前......等等,您必须比较和转换/比较所有子字符串,直到获得订单。

标签: java string sorting alphabetical


【解决方案1】:

你可以这样做:

list.sort(Comparator.comparing(YourClass::removeNumbers).thenComparing(YourClass::keepNumbers));

这是两种方法:

private static String removeNumbers(String s) {
    return s.replaceAll("\\d", "");
}

private static Integer keepNumbers(String s) {
    String number = s.replaceAll("\\D", "");
    if (!number.isEmpty()) {
        return Integer.parseInt(number);
    }
    return 0;
}

对于以下数据:

List<String> list = new ArrayList<>();
list.add("TEXT-2");
list.add("TEST-6");
list.add("TEST-1");
list.add("109");
list.add("TE");
list.add("TESTT-0");
list.add("TES-100");

这是排序结果:

[109, TE, TES-100, TEST-1, TEST-6, TESTT-0, TEXT-2]

【讨论】:

  • 不错的解决方案。但请注意,在您的解决方案中,数字不是与其数值进行比较,而是与其字符串表示进行比较。例如TEST-12 &lt; Test-3。我会将 keepNumbers 的返回值更改为 int。
  • @Eritrean 我已经更新了keepNumbers 方法。现在它返回数字的Integer 表示,而不是String
  • 我已经尝试过您的解决方案,看起来效果很好。我会做更多的测试,然后将其设置为正确的解决方案。谢谢
  • @CaioAmbrosio 当然,我希望它有效。否则,您可能需要根据您的数据对其进行一些调整。
  • @CaioAmbrosio 很高兴我的回答对您有所帮助。
【解决方案2】:

如果我是对的,问题出在你的字符'-'上,通过使用 string.replace("-","") 然后你可以继续正常排序,让字符串保持原样进行排序,希望它能按您的预期工作。

String num = s.replaceAll("\\D", "").replace("-","");

如果你没有任何负值它应该可以工作,即使然后应用正则表达式来检查它是一个负数还是包含'-'的字符串。

【讨论】:

  • "\\D" 也包括-。您不必重做。
【解决方案3】:

这是一个比较方法,我们用于对在任何位置可以包含多个数字的字符串(例如,"TEST-10.5""TEST-42-Subsection-3" 等字符串)进行排序:

boolean isDigit( char c ) {
  return '0' <= c && c <= '9';
}

int compare( String left, String right, Collator collator ) {
  if ( left == null || right == null ) {
    return left == right ? 0 : ( left == null ? -1 : 1 );
  }

  String s1 = left.trim();
  String s2 = right.trim();

  int l1 = s1.length();
  int l2 = s2.length();
  int i1 = 0;
  int i2 = 0;
  while ( i1 < l1 && i2 < l2 ) {
    boolean isSectionNumeric = isDigit( s1.charAt( i1 ) );
    if ( isSectionNumeric != isDigit( s2.charAt( i2 ) ) ) {
      // one of the strings now enters a digit section and one is in a text section so we're done 
      //switch to -1 : 1 if you want numbers before text
      return isSectionNumeric ? 1 : -1;
    }

    // read next section
    int start1 = i1;
    int start2 = i2;
    for ( ++i1; i1 < l1 && isDigit( s1.charAt( i1 ) ) == isSectionNumeric; ++i1 ){/* no operation */}
    for ( ++i2; i2 < l2 && isDigit( s2.charAt( i2 ) ) == isSectionNumeric; ++i2 ){/* no operation */}
    String section1 = s1.substring( start1, i1 );
    String section2 = s2.substring( start2, i2 );

    // compare the sections:
    int result =
        isSectionNumeric ? Long.valueOf( section1 ).compareTo( Long.valueOf( section2 ) )
      : collator == null ? section1.trim().compareTo( section2.trim() )
      :                    collator.compare( section1.trim(), section2.trim() );

    if ( result != 0 ) {
      return result;
    }

    if ( isSectionNumeric ) {
      // skip whitespace
      for (; i1 < l1 && Character.isWhitespace( s1.charAt( i1 ) ); ++i1 ){/* no operation */}
      for (; i2 < l2 && Character.isWhitespace( s2.charAt( i2 ) ); ++i2 ){/* no operation */}
    }
  }

  // we've reached the end of both strings but they still are equal, so let's do a "normal" comparison
  if ( i1 == l1 && i2 == l2 ) {      
    return collator == null ? left.compareTo( right ) : collator.compare( left, right );
  }

  // we've reached the end of only one string, so the other must either be greater or smaller
  return ( i1 == l1 )? -1 : 1;
}

这个想法是将字符串“拆分”为“文本”和数字部分,并逐个比较这些部分。支持小数,因为整数、小数点和小数部分将是 3 个单独比较的部分。

这基本上类似于将字符串拆分为子字符串数组并比较每个相应索引处的元素。那么你有以下几种情况:

  • 两个元素都是文本:做一个普通的字符串比较
  • 两个元素都代表数字:解析和比较数字
  • 一个元素是一个文本,另一个代表一个数字:决定哪个更大
  • 我们已经到达两个字符串的末尾,但所有元素都是相等的:如果可能,我们可以完成或对整个字符串进行“正常”比较以获得顺序
  • 我们只到达了一个字符串的结尾,它们仍然相等:据报道,越长的字符串越大(一定是因为内容更多;))

请注意,这只是我们的做法,还有其他方法(例如不跳过空格的方法)。

【讨论】:

    猜你喜欢
    • 2019-05-26
    • 2011-03-07
    • 1970-01-01
    • 2011-07-23
    • 1970-01-01
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    相关资源
    最近更新 更多