配列のソート2
JavaプログラマのためのObjective-Cにおける配列ソート解説。第2弾。
前回は、NSSortDescriptorクラスを使った配列のソート方法を紹介した。
内容的にはシンプルであったとは思うが、Key-Valueコーディングや、セレクタなど、Objective-C独特の手法が絡んでいたので、今回は、よりJavaに近い書き方でソートする方法を紹介する。
たとえば、ある自然な順序付けを持たないクラス MyClass 内のフィールド x の昇順にListをソートしたい場合、Javaでは以下のように記述できる。
public class MyClass { private int x; public MyClass(int x) { this.x = x; } public int x() { return x; } public static void main(String[] args) { java.util.List<MyClass> list = new java.util.ArrayList<>(); list.add(new MyClass(8)); list.add(new MyClass(5)); list.add(new MyClass(4)); list.add(new MyClass(6)); list.add(new MyClass(3)); java.util.Collections.sort(list, new java.util.Comparator<MyClass>() { @Override public int compare(MyClass o1, MyClass o2) { if (o1.x < o2.x) { return -1; } else if (o1.x > o2.x) { return 1; } else { return 0; } } }); for (MyClass o : list) { System.out.println(o.x()); } } }
これとほぼ同じことをObjective-Cでやるには、java.util.Comparatorインターフェイスの代わりに、高階関数を使えばよい。
以下のように実装できる。
@interface MyClass : NSObject @property(assign, nonatomic, readwrite) int x; +(MyClass*)create:(int)x; @end @implementation MyClass @synthesize x; +(MyClass*)create:(int)x { MyClass* ins = [[[MyClass alloc] init] autorelease]; if (ins) { ins.x = x; } return ins; } @end int main(int argc, const char** argv) { @autoreleasepool { NSArray* list = [NSArray arrayWithObjects: [MyClass create:8], [MyClass create:5], [MyClass create:4], [MyClass create:6], [MyClass create:3], nil]; list = [list sortedArrayUsingComparator:^(MyClass* o1, MyClass* o2){ if (o1.x < o2.x) { return (NSComparisonResult)NSOrderedAscending; } else if (o1.x > o2.x) { return (NSComparisonResult)NSOrderedDescending; } else { return (NSComparisonResult)NSOrderedSame; } }]; for (MyClass* o in list) { printf("%d\n", o.x); } } }
Javaで無名のComparator実装クラスを定義している箇所が、Objective-Cのブロック構文に置き換えられているだけで、記述している内容はほぼ同じだ。人によっては、前回紹介したNSSortDescriptorよりも、こちらの方がわかり易いということもあるだろう。
とはいえ、多くの場合、NSSortDescriptorを使った方がスマートに記述できるとは思う。
単純な1プロパティの比較のみでは順序付けができないといった場合には、こちらのComparatorを使うことになるだろう。