2016-08-18 4 views
1

У меня есть компаратор, который сортирует массив строк, содержащих буквы и цифры, но не может, как представляется, идентифицировать регулярное выражение, которое сортирует их так, как я и ищу.Сортировка строк, которые содержат целые числа с компаратором

Я использовал this question в качестве ссылки для моего компаратора.

array={string-a01,string-a20,string-a100,string-b01,string-b20,string-b100,string-c01,string-c20,string-c100 etc.} 

Collections.sort(array, new Comparator<String>(){  
    public int compare(String o1, String o2) { 
     return extractInt(o1) - extractInt(o2); 
    } 

    int extractInt(String s) { 
     String num = s.replaceAll("\\D", ""); 
     return num.isEmpty() ? 0 : Integer.parseInt(num); 
    } 
}); 

for (String element : array) { 
    System.out.println(element); 
} 

Перед введением компаратор выход был:
string-a01, string-a100, string-a20, string-b01, string-b100, string-b20, string-c01, string-c20, string-c100

Вывод, что этот код производит это:
string-a01, string-b01, string-c01 string-a20, string-b20, string-c20 string-a100, string-b100, string-c100

Выход я хотел бы, чтобы произвести это:
string-a01, string-a20, string-a100, string-b01, string-b20, string-b100, string-c01, string-c20, string-c100


EDIT: Отредактировано для пояснений. Массив был изменен и выведен до добавления компаратора.

ответ

2

Предполагая, что string часть на самом деле что-то другое, чем просто "string".Вы можете извлечь письмо часть концовки, и значную часть, и сравнить их с помощью композитного Comparator:

String[] array = { "string-a20", "string-a01", "string-b01", 
    "string-b20", "string-c01", "string-c20", 
    "string-a100", "string-b100", "string-c100" }; 

Pattern p = Pattern.compile("^.*?-([A-Za-z]+)(\\d+)$"); 

List<String> result = Arrays.stream(array) 
    .map(p::matcher) 
    .filter(Matcher::find) 
    .sorted(Comparator.comparing((Matcher m) -> m.group(1)) // Compare the letter part 
     .thenComparingInt(m -> Integer.parseInt(m.group(2)))) // Compare the number part 
    .map(m -> m.group(0)) // Map back to String 
    .collect(Collectors.toList()); 

System.out.println(result); 

Выхода:

[string-a01, string-a20, string-a100, string-b01, string-b20, string-b100, string-c01, string-c20, string-c100] 

домаДомого (с недостатком наличия воссоздать Matcher с):

Arrays.sort(array, new Comparator<String>() { 

    Pattern p = Pattern.compile("^.*?-([A-Za-z]+)(\\d+)$"); 

    @Override 
    public int compare(String o1, String o2) { 
     Matcher m1 = p.matcher(o1); 
     Matcher m2 = p.matcher(o2); 

     if(!(m1.find() && m2.find())) 
      return 0; // Or throw a format exception 

     int comparison = m1.group(1).compareTo(m2.group(1)); 
     return comparison != 0 
      ? comparison 
      : Integer.compare(Integer.parseInt(m1.group(2)), Integer.parseInt(m2.group(2))); 
    } 

}); 
+1

Я сделал еще одно обновление, потому что основной вопрос не просил все, что я искал. Дело в том, что он заказывает так: b01, b100, b11, c01, c100, c11 ...массив генерируется динамически и не понимает, что проблема не возникнет с исходным массивом, опубликованным – Jon

+1

@ Jon, Да, я изучал это, но это немного сложнее. –

+1

@ Jon, я изменил ответ. Это работает? –

1

Вы удаляете алфавитные символы в свой метод extractInt, поэтому вы не сможете использовать их в сравнении.

Вы должны просто отсортировать их неComparator, который будет сортировать их, используя по умолчанию, лексикографический алгоритм сортировки (java.lang.String орудия Comparable<String>).

Пример

// test array 
String[] s = {"string-a01","string-a01","string-b01","string-b02","string-c02","string-c02"}; 

// sorting with null Comparator, will sort if the type implements Comparable - 
// which String does 
Arrays.sort(s); 

// printing in human-readable form 
System.out.println(
    Arrays.toString(s) 
); 

Выход

[string-a01, string-a01, string-b01, string-b02, string-c02, string-c02] 

Примечания

  • Если вы хотите удалить дубликаты (ИБК ч может быть ваше намерение от вопроса - не ясно), добавьте элементы массива в TreeSet вместо:

    Set<String> deduplicated = new TreeSet<>(Arrays.asList(s)); 
    
  • Если ваш алгоритм сортировки должен действовать таким образом, что 2 предшествует 12, то вам нужно извлечь целое число значение , не удаляя его из элементов, и сравните его, только если остальная часть String s равна.

+0

Почему бы вам не использовать лямбда, проще – GingerHead

+0

@GingerHead, как с помощью Java 8 потока API любой "проще", чем 'Arrays.sort', учитывая этот контекст? – Mena

+0

Например, 'Arrays.sort (s, (a, b) -> a.length() - b.length());' – GingerHead

1

Это звучит, как вы хотите заказать струны на т он «ведущие строки», т. е. все до цифр; если ведущие строки равны, то сравните их с последующими цифрами.

Чтобы разбить строку на ее «строковые» и «целые» части, вы можете сначала «первую конечную цифру», то есть позицию первого символа в строке, где между ней нет конец строки:

int firstTrailingDigit(String s) { 
    int i = s.length(); 
    while (i > 0 && Character.isDigit(s.charAt(i - 1))) { 
    --i; 
    } 
    return i; 
} 

Вы можете использовать это в вашем компаратора:

public int compare(String a, String b) { 
    int ftdA = firstTrailingDigit(a); 
    int ftdB = firstTrailingDigit(b); 

    // Get the leading strings, and compare. 
    String sA = a.substring(0, ftdA); 
    String sB = b.substring(0, ftdB); 
    int compareStrings = sA.compareTo(sB); 
    if (compareStrings != 0) { 
    // If they're not equal, return the result of the comparison. 
    return compareStrings; 
    } 

    // Get the trailing numbers from the strings, and compare. 
    int iA = Integer.parseInt(a.substring(ftdA)); 
    int iB = Integer.parseInt(b.substring(ftdB)); 
    return Integer.compare(iA, iB); 
} 

Ideone demo

Вход:

String[] array = {"string-a01","string-a20","string-a100","string-b01","string-b20","string-b100","string-c01","string-c20","string-c100"}; 

Выход:

[string-a01, string-a20, string-a100, string-b01, string-b20, string-b100, string-c01, string-c20, string-c100] 
Смежные вопросы