Сортировка строк, содержащих число в Java
У меня проблема с компаратором по умолчанию для строк (в SortedSet). Проблема в том, что компаратор по умолчанию не сортирует хорошую строку, содержащую числа, то есть: в наборе у меня есть:
room1, room2, room100
Естественный порядок должен быть таким, как указано выше, но в наборе у меня есть:
room1, room100, room2
Я знаю, почему это так, но я не знаю, как это изменить.
6 ответов
Попробуйте этот компаратор, который удаляет все нецифровые символы, а затем сравнивает оставшиеся символы как числа:
Collections.sort(strings, 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 0 if no digits found
return num.isEmpty() ? 0 : Integer.parseInt(num);
}
});
Вот тест:
public static void main(String[] args) throws IOException {
List<String> strings = Arrays.asList("room1", "foo", "room2", "room100", "room10");
Collections.sort(strings, 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 0 if no digits found
return num.isEmpty() ? 0 : Integer.parseInt(num);
}
});
System.out.println(strings);
}
Выход:
[foo, room1, room2, room10, room100]
Использовал @bohemian ответ. Просто немного улучшилось. Это сработало для меня очень хорошо..
Collections.sort(asdf, new Comparator<String>() {
public int compare(String o1, String o2) {
String o1StringPart = o1.replaceAll("\\d", "");
String o2StringPart = o2.replaceAll("\\d", "");
if(o1StringPart.equalsIgnoreCase(o2StringPart))
{
return extractInt(o1) - extractInt(o2);
}
return o1.compareTo(o2);
}
int extractInt(String s) {
String num = s.replaceAll("\\D", "");
// return 0 if no digits found
return num.isEmpty() ? 0 : Integer.parseInt(num);
}
});
Попробуй это. Я предполагал, что у вас всегда будет "комната" в начале вашей строки.
List<String> list = Arrays.asList("room1", "room100", "room2");
Collections.sort(list, new Comparator<String>()
{
@Override
public int compare(String o1, String o2)
{
return new Integer(o1.replaceAll("room", ""))
.compareTo(new Integer(o2.replaceAll("room", "")));
}
});
Вот мой Comparator
реализация для такого рода:
(строки могут начинаться с любых символов)
public class StringNumberComparator implements Comparator<String>{
@Override
public int compare(String o1, String o2) {
int i1 = this.getRearInt(o1);
int i2 = getLeadingInt(o2);
String s1 = getTrailingString(o1);
String s2 = getTrailingString(o2);
if(i1==i2)
return s1.compareTo(s2);
if(i1>i2)
return 1;
else if(i1<i2)
return -1;
return 0;
}
private int getRearInt(String s) {
s=s.trim();
int i=Integer.MAX_VALUE;
try {
i = Integer.parseInt(s.split("[^0-9]+")[1]);
} catch(ArrayIndexOutOfBoundsException e) {
} catch(NumberFormatException f) {
return i;
}
return i;
}
private String getTrailingString(String s) {
return s.replaceFirst("[0-9]", "");
}
}
Ленивой альтернативой было бы заставить компаратор String работать, не делая ничего лишнего (определяя свой собственный компаратор). Вы можете получить это, заполнив нулями числа в вашей строке следующим образом: room0001, room0002, room0100
тогда будет работать компаратор String по умолчанию. Однако вам необходимо знать максимальное значение числа, чтобы вы могли соответствующим образом адаптировать заполнение.
Вы можете реализовать компаратор и передать его конструктору множеств. См. http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Comparator.html.
Если все ваши строки представлены в виде комнаты [число], вы можете убрать "комнату", проанализировать число и сравнить по нему.
В качестве альтернативы - вы можете хранить целые числа в наборе и печатать их с префиксом "комната".