stackoverflow上關(guān)于Objective-C關(guān)注度比較高的問題系列
鏈接
按照NSArray內(nèi)部的某個(gè)對(duì)象排序
原文鏈接How do I sort an NSMutableArray with custom objects in it?
本文Github鏈接
關(guān)鍵詞
Sort
NSArray
NSSortDescriptor
我想實(shí)現(xiàn)的事情看起來很簡單,但在網(wǎng)上卻找不到答案。有一個(gè)NSMutableArray or NSArray數(shù)組,數(shù)組中的元素是Person對(duì)象。我想按照Person.birthDate來排序,birthDate的類型是NSDate。
我猜測實(shí)現(xiàn)的方式的偽代碼應(yīng)該如下:
NSArray *sortedArray = [drinkDetails sortedArrayUsingSelector:@selecrot(???)];
排序方法
1) 對(duì)象中實(shí)現(xiàn)Compare方法(Compare method)
首先,在Person的implementation中實(shí)現(xiàn) compare method:
- (NSComparisonResult)compare:(Person *)person {
return [self.birthDate compare:person.birthDate];
}
然后在需要排序的地方調(diào)用- (NSArray<ObjectType> *)sortedArrayUsingSelector:(SEL)comparator。
NSArray *sortedArray = [array sortedArrayUsingSelector:@selector(compare:)];
2)NSSortDescriptor
NSSortDescriptor的key屬性為需要排序?qū)ο蟮哪硞€(gè)屬性(此處用到了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,可以很輕松的實(shí)現(xiàn)多個(gè)key復(fù)合排序(此處的復(fù)合排序指:當(dāng)?shù)谝粋€(gè)標(biāo)準(zhǔn)的兩個(gè)值相同時(shí),此時(shí)需要第二個(gè)標(biāo)準(zhǔn)來參與排序,第二個(gè)標(biāo)準(zhǔn)也相同的話就需要引入第三個(gè)標(biāo)準(zhǔn),以此類推)。
優(yōu)先比較sortDescriptors的第一項(xiàng),然后第二項(xiàng),然后第三項(xiàng),以此類推。
關(guān)于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:的方法實(shí)現(xiàn)起來會(huì)比使用NSSortDescriptor稍微快一點(diǎn),后者是依賴于KVC。NSSortDescriptor的主要優(yōu)勢在于它可以直接使用數(shù)據(jù)來定義排序的標(biāo)準(zhǔn)即可,不需要自己來寫排序。在上一個(gè)例子中,只需要指定key和是否為升序兩個(gè)參數(shù)就可以排序了,block方法還需要自己來compare first and second。
當(dāng)然在compare first和second的時(shí)候再復(fù)雜一些,比較一下Person的其他屬性也能達(dá)到NSSortDescriptor的復(fù)合排序。
結(jié)果及分析
我將以上三種方法各運(yùn)行一次得到的三次結(jié)果如下:
1)對(duì)象中實(shí)現(xiàn)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. 對(duì)象中實(shí)現(xiàn)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
分析結(jié)果
- 最終數(shù)組的順序是按照
birthDate的由小到大排序; - 最后兩個(gè)數(shù)據(jù)的
birthDate相同卻沒有按照name的升序排列; - 代碼執(zhí)行時(shí)間duration = 0.000061。
注:在ASCII碼中小寫字母是排在大寫字母后面的。字母a對(duì)應(yīng)的十進(jìn)制數(shù)是97,字母A對(duì)應(yīng)的十進(jìn)制數(shù)是65,字母C(此處C大寫)對(duì)應(yīng)的十進(jìn)制數(shù)是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
分析結(jié)果
- 最終數(shù)組的順序是按照
birthDate的由小到大排序; - 最后兩個(gè)數(shù)據(jù)的
birthDate相同,然后按照name的升序排列。 - 代碼執(zhí)行時(shí)間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
分析結(jié)果
- 最終數(shù)組的順序是按照
birthDate的由小到大排序; - 最后兩個(gè)數(shù)據(jù)的
birthDate相同卻沒有按照name的升序排列; - 代碼執(zhí)行時(shí)間duration = 0.000062。
4)性能分析
在計(jì)算duration的時(shí)候,不同電腦,得到的結(jié)果是不一樣的,但是數(shù)字對(duì)應(yīng)的數(shù)量級(jí)應(yīng)該是一致的。
三次的時(shí)間分別如下:
- duration = 0.000061
- duration = 0.000341
- duration = 0.000062
可以看出前方法1和3的在同一個(gè)數(shù)量級(jí)上,第二種方法明顯其他兩種大一個(gè)數(shù)量級(jí)。
我們利用for循環(huán)將每部分代碼運(yùn)行1,000,000次,得到的結(jié)果如下:
- duration = 1.128369
- duration = 3.287778
- duration = 1.433518
依然可以得到相同的結(jié)果。1秒和3秒之間的差距,感知是很明顯的。
5)結(jié)論
- 如果沒有復(fù)合排序,可優(yōu)先選擇block方法。代碼較為集中看起來邏輯較清晰;
- 如果有復(fù)合排序,數(shù)據(jù)量不大的時(shí)候可以使用NSSortDescriptor;數(shù)據(jù)量較大時(shí)建議選擇block方法。