按照NSArray內部的某個物件排序

weixin_34342992發表於2018-05-22

stackoverflow上關於Objective-C關注度比較高的問題系列
連結

按照NSArray內部的某個物件排序

原文連結How do I sort an NSMutableArray with custom objects in it?

本文Github連結

關鍵詞

Sort
NSArray
NSSortDescriptor

我想實現的事情看起來很簡單,但在網上卻找不到答案。有一個NSMutableArray or NSArray陣列,陣列中的元素是Person物件。我想按照Person.birthDate來排序,birthDate的型別是NSDate

我猜測實現的方式的虛擬碼應該如下:

NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selecrot(???)];

排序方法

1) 物件中實現Compare方法(Compare method)

首先,在Person的implementation中實現 compare method:

- (NSComparisonResult)compare:(Person *)person {
    return [self.birthDate compare:person.birthDate];
}

然後在需要排序的地方呼叫- (NSArray<ObjectType> *)sortedArrayUsingSelector:(SEL)comparator

NSArray *sortedArray = [array sortedArrayUsingSelector:@selector(compare:)];

2)NSSortDescriptor

NSSortDescriptorkey屬性為需要排序物件的某個屬性(此處用到了KVC);ascending用於指定升序還是降序,YES-> 生序,NO->降序 ``

NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"birthDate" ascending:YES];
NSSortDescriptor *nameSortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES];
    
NSArray *sortDescriptors = @[sortDescriptor,nameSortDescriptor];
NSArray *sortedArray = [array sortedArrayUsingDescriptors:sortDescriptors];

通過向array增加不同的key,可以很輕鬆的實現多個key複合排序(此處的複合排序指:當第一個標準的兩個值相同時,此時需要第二個標準來參與排序,第二個標準也相同的話就需要引入第三個標準,以此類推)。

優先比較sortDescriptors的第一項,然後第二項,然後第三項,以此類推。

關於NSSortDescriptor可參考檔案

3) Blocks

Mac OS X 10.6 和 iOS4之後也可以使用block來排序:

NSComparator sortComparator = ^NSComparisonResult(id a, id b) {
    NSDate *first = [(Person *)a birthDate];
    NSDate *second = [(Person *)b birthDate];
    return [first compare:second];
};
NSArray *sortedArray = [array sortedArrayUsingComparator:sortComparator];

一般來說,基於block和-compare:的方法實現起來會比使用NSSortDescriptor稍微快一點,後者是依賴於KVCNSSortDescriptor的主要優勢在於它可以直接使用資料來定義排序的標準即可,不需要自己來寫排序。在上一個例子中,只需要指定key是否為升序兩個引數就可以排序了,block方法還需要自己來compare first and second

當然在compare firstsecond的時候再複雜一些,比較一下Person的其他屬性也能達到NSSortDescriptor的複合排序。

結果及分析

我將以上三種方法各執行一次得到的三次結果如下:

1)物件中實現Compare方法(Compare method)

original array:

name:       birthDate:          age:
 
Jixin       1526984251.000000    14 
Alex        1526923252.000000    22 
alex        1527983288.000000    21 
Chandler    1527983288.000000    23 
Kobe        1526083230.000000    24 
1Day        1426983253.000000    7 

-----------------------

1. 物件中實現Compare方法:

sorted array:
name:       birthDate:          age:
 
1Day        1426983253.000000    7 
Kobe        1526083230.000000    24 
Alex        1526923252.000000    22 
Jixin       1526984251.000000    14 
alex        1527983288.000000    21 
Chandler    1527983288.000000    23 

-----------------------
duration = 0.000061

Program ended with exit code: 0

分析結果

  1. 最終陣列的順序是按照birthDate的由小到大排序;
  2. 最後兩個資料的birthDate相同卻沒有按照name的升序排列;
  3. 程式碼執行時間duration = 0.000061。

注:在ASCII碼中小寫字母是排在大寫字母后面的。字母a對應的十進位制數是97,字母A對應的十進位制數是65,字母C(此處C大寫)對應的十進位制數是67)。

2)NSSortDescriptor

original array:

name:       birthDate:          age:
 
Jixin       1526984251.000000    25 
Alex        1526923252.000000    1 
alex        1527983288.000000    12 
Chandler    1527983288.000000    21 
Kobe        1526083230.000000    6 
1Day        1426983253.000000    4 

-----------------------

2. NSSortDescriptor方法:

sorted array:
name:       birthDate:          age:
 
1Day        1426983253.000000    4 
Kobe        1526083230.000000    6 
Alex        1526923252.000000    1 
Jixin       1526984251.000000    25 
Chandler    1527983288.000000    21 
alex        1527983288.000000    12 

-----------------------
duration = 0.000341

Program ended with exit code: 0

分析結果

  1. 最終陣列的順序是按照birthDate的由小到大排序;
  2. 最後兩個資料的birthDate相同,然後按照name的升序排列。
  3. 程式碼執行時間duration = 0.000341。

3)Blocks

original array:

name:       birthDate:           age:
 
Jixin       1526984251.000000    21 
Alex        1526923252.000000    23 
alex        1527983288.000000    12 
Chandler    1527983288.000000    3 
Kobe        1526083230.000000    27 
1Day        1426983253.000000    12 

-----------------------

3. Block方法:
 
sorted array:
name:       birthDate:          age:
 
1Day        1426983253.000000    12 
Kobe        1526083230.000000    27 
Alex        1526923252.000000    23 
Jixin       1526984251.000000    21 
alex        1527983288.000000    12 
Chandler    1527983288.000000    3 

-----------------------
duration = 0.000062

Program ended with exit code: 0

分析結果

  1. 最終陣列的順序是按照birthDate的由小到大排序;
  2. 最後兩個資料的birthDate相同卻沒有按照name的升序排列;
  3. 程式碼執行時間duration = 0.000062。

4)效能分析

在計算duration的時候,不同電腦,得到的結果是不一樣的,但是數字對應的數量級應該是一致的。

三次的時間分別如下:

  1. duration = 0.000061
  2. duration = 0.000341
  3. duration = 0.000062

可以看出前方法1和3的在同一個數量級上,第二種方法明顯其他兩種大一個數量級。

我們利用for迴圈將每部分程式碼執行1,000,000次,得到的結果如下:

  1. duration = 1.128369
  2. duration = 3.287778
  3. duration = 1.433518

依然可以得到相同的結果。1秒和3秒之間的差距,感知是很明顯的。

5)結論

  1. 如果沒有複合排序,可優先選擇block方法。程式碼較為集中看起來邏輯較清晰;
  2. 如果有複合排序,資料量不大的時候可以使用NSSortDescriptor;資料量較大時建議選擇block方法。

相關文章