ArrayListの簡単なソートプログラムを次に示します。
ArrayList<String> list = new ArrayList<String>();
list.add("1_Update");
list.add("11_Add");
list.add("12_Delete");
list.add("2_Create");
Collections.sort(list);
for (String str : list) {
System.out.println(str.toString());
}
このプログラムの出力は次のように期待していました。
1_Update
2_Create
11_Add
12_Delete
しかし、このプログラムを実行すると、次のような出力が得られます。
11_Add
12_Delete
1_Update
2_Create
これはなぜですか、また、予想される出力に示されているようにArrayListをソートするにはどうすればよいですか?
カスタムコンパレータを作成できます。
Collections.sort(list, new Comparator<String>() {
public int compare(String a, String b) {
return Integer.signum(fixString(a) - fixString(b));
}
private int fixString(String in) {
return Integer.parseInt(in.substring(0, in.indexOf('_')));
}
});
このタイプのデータを文字列としてソートすると、数字を含む文字自体が比較されます。たとえば、「1」で始まる文字列はすべて一緒になります。したがって、順序はこれと同様になります...
1 10100 2 20200
文字列の先頭にある可変長の数字など、文字列のサブセットに意味を割り当てるソートを「実現」することはありません。数字を文字列として並べ替える場合、最大数をカバーするために必要なだけゼロを左にパディングすると役立ちますが、例のようにデータを制御しない場合、実際には問題を解決しません。その場合、並べ替えは...
001 002 010 020 100 200
数値ではなくテキスト(アルファベット順)としてソートされます。これを回避するには、nsayerの回答で提案されているカスタムコンパレータを実装します。
辞書編集の比較を行っています。各文字列の最初の文字を並べ替えて比較します。次に、同じ最初の文字を持つ2番目の文字列を比較します。 「_」文字を数字と比較すると、8> 7やa> 9のような単一の数字よりも値が大きくなります。数値比較ではなく文字比較を行っていることに注意してください。
スクリプト名を変更するよりも優れた独自のカスタムソートルーティングを実装する方法があります。
スクリプト名の変更がオプションの場合、これにより他のスクリプトツールの使用が許可される場合があります。 1つの形式は
01_create_table.sql 02_create_index.sql 11_assign_privileges.sql
最初の2桁を2文字に保つことにより、辞書式比較が機能します。
Collections.sort()メソッドのドキュメントによると:
要素の自然順序付けに従って、指定されたリストを昇順でソートします。
これは、文字列の場合、アルファベット順にリストを取得することを意味します。文字列11_assign_privileges.sqlは文字列1_create_table.sqlの前にあり、12_07_insert_static_data.sqlは1_create_table.sqlなどの前にあります。したがって、プログラムは正常に動作しています。
Collection.sort()を任意にソートするには、次を使用できます。
Collections.sort(List list, Comparator c)
次に、文字列を分割し、最初に数値に基づいてソートし、次に残りに基づいてソートするComparatorを実装します。
説明は、文字列が文字列としてソートされているということであるとすでに指摘しており、数はすでに自然順序の文字列比較に注意を向けています。このコンパレータを自分で記述するのは素晴らしい練習であり、テスト駆動開発を実践する絶好の機会であると付け加えます。 Code CampでTDDのデモに使用しました。スライドとコードは こちら です。
IComparableインターフェイスを追加してから、特定のプロパティで並べ替えることができます。たとえば、店舗のアイテムのコレクションがある場合は、価格やカテゴリなどで並べ替えることができます。名前で並べ替える場合の例を次に示します。
arrayListがアイテムのnameプロパティによってどのようにソートされるかに注意してください。 IComparableを追加しない場合、sortメソッドを使用するとエラーがスローされます。
static void Main(string[] args)
{
ArrayList items = new ArrayList();
items.Add(new Item("book", 12.32));
items.Add(new Item("cd", 16.32));
items.Add(new Item("bed", 124.2));
items.Add(new Item("TV", 12.32));
items.Sort();
foreach (Item temp in items)
Console.WriteLine("Name:{0} Price:{1}", temp.name, temp.price);
Console.Read();
}
class Item: IComparable
{
public string name;
public double price;
public Item(string _name, double _price)
{
this.name = _name;
this.price = _price;
}
public int CompareTo(object obj)
{
//note that I use the name property I may use a different one
int temp = this.name.CompareTo(((Item)obj).name);
return temp;
}
}
文字列はアルファベット順にソートされ、アンダースコア文字は数字の文字の後にあるためです。目的の結果を得るには、「自然順序」を実装するコンパレータを提供する必要があります。
文字列比較アルゴリズムは、一度に各文字を比較します。 1
は2
の前にソートされます。後に1
または2
が続くことは問題ではありません。
したがって、100
は2
の前にソートされます。この動作が望ましくない場合は、このケースを処理する比較アルゴリズムが必要です。
他の人が述べたように、要素はデフォルトでアルファベット順にソートされます。解決策は、具体的なJava.util.Comparatorクラスを定義し、それをsortメソッドの2番目の引数として渡すことです。コンパレータは、文字列の先頭の整数を解析して比較する必要があります。