文字列 (SortedSet 内) のデフォルトのコンパレータに問題があります。問題は、デフォルトのコンパレータが数字を含む適切な文字列をソートしないことです。つまり、セットには次のものがあります。
room1, room2, room100
自然な順序付けは上記のようにする必要がありますが、セットには次のものがあります。
room1, room100, room2
原因はわかっているのですが、変更方法がわかりません。
数字以外のすべての文字を削除し、残りの文字を数字として比較するこのコンパレータを試してください。
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.2", "foo1.1", "foo", "room2.3", "room100.999", "room10", "room.3");
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]
数値が小数の場合 (これも Java 8+ スタイルを示しています):
public static void main(String[] args) {
List<String> strings = Arrays.asList("room1.2", "foo1.1", "room2.3", "room100.999", "room10", "room.3");
Collections.sort(strings, Comparator.comparing(Application::extractDouble));
System.out.println(strings);
}
static double extractDouble(String s) {
String num = s.replaceAll("[^\\d.]", "");
// return 0 if no digits found
return num.isEmpty() ? 0 : Double.parseDouble(num);
}
結果:
[foo, room.3, foo1.1, room1.2, room2.3, room10, room100.999]
@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]", "");
}
}
コンパレータを実装して、それを set コンストラクタに渡すことができます。http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Comparator.htmlを参照してください。
すべての文字列が room[number] の形式である場合、「部屋」を取り除いて数字を解析し、比較することができます。
または、整数をセットに保存し、「部屋」プレフィックスを付けて印刷することもできます。
怠惰な代替手段は、余分なことを何もせずに文字列コンパレーターを機能させることです (独自のコンパレーターを定義します)。次のように、文字列内の数値をゼロでパディングすることで、それを実現できます。room0001, room0002, room0100
その後、デフォルトの文字列コンパレータが機能します。ただし、それに応じてパディングを調整できるように、最大数値を知る必要があります。