配列のソート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を使うことになるだろう。