不是所有可變的集合都叫做 MutableCollection

作者:Ole Begemann,原文鏈接感论,原文日期:2017/02/06
譯者:Cwift;校對:walkingway;定稿:CMB

Collection 協(xié)議是 Swift 集合類型的根基娘锁。除了 Collection,標準庫還提供了另外四個協(xié)議饺鹃,用來擴展集合類型的功能莫秆。這些協(xié)議改進了 Collection —— 遵守任何一個協(xié)議的對象也必須遵守 Collection。

swift.png

它們分別是:

RandomAccessCollection 協(xié)議改進了 BidirectionalCollection 協(xié)議,因為前者是后者的嚴格超集 —— 任何可以有效地跳轉(zhuǎn)到任意索引的集合都可以向后遍歷齿拂。RandomAccessCollection 沒有基于 BidirectionalCollection 提供新的 API驳规;也就是說前者能做的事情,后者都可以做到署海。然而吗购,RandomAccessCollection 嚴格的特性保證了遵守者中的算法只能通過隨機元素訪問來實現(xiàn)。

你可能認為 RangeReplaceableCollection 應(yīng)該類似地改進于 MutableCollection砸狞,因為突變可以用插入和刪除來建模捻勉,但事實并不是這樣。這兩個協(xié)議是在同一層的 —— 一些類型只符合 MutableCollection(例如 UnsafeMutableBufferPointer)刀森,一些只適用于RangeReplaceableCollection(例如String.CharacterView)踱启,只有一部分同時遵守了兩者(Array 和 Data)。

稍后將通過一些示例來解釋原因研底。

Set

MutableCollection

MutableCollection 支持元素在原位置修改埠偿。該協(xié)議在 Collection 的基礎(chǔ)上新增的 API 是下標必須提供一個 setter。

Set 是一個 Collection榜晦,并且它當然是可變的冠蒋。所以應(yīng)該遵守 MutableCollection 協(xié)議,這看起來合情合理抖剿,但是事實并非如此。主要是因為協(xié)議的語義识窿。MutableCollection 允許更改集合元素的值斩郎,但是協(xié)議的文檔規(guī)定:突變必須既不改變集合的長度脓恕,也不能改變元素的順序。Set 不能滿足任何一項乃秀。

首先來說保留長度的問題。Set 不能包含重復的元素,因此首装,如果你使用一個 Set 中已經(jīng)存在的值來替換某個元素涧尿,則該 Set 會在變異后減少一個元素。

Set 也是一種無序的集合 —— 使用它的時候元素的順序總是無序的迷雪。不過在其內(nèi)部限书,Set 依據(jù)其構(gòu)成的規(guī)則具有穩(wěn)定的順序。當你通過下標更改 MutableCollection 的遵守者時章咧, 被更改元素的索引必須保持不變倦西,即索引在集合中所指示的位置不能改變。Set 不能滿足這個要求赁严,因為一個 SetIndex 指向的是桶狀(bucket)的內(nèi)部存儲結(jié)構(gòu)扰柠,當內(nèi)部元素變異時,該存儲桶會發(fā)生變化疼约。

RangeReplaceableCollection

RangeReplaceableCollection 向 Collection 中增加了兩個要求:一個空的構(gòu)造器以便可以創(chuàng)建一個新的空集合卤档,以及 replaceSubrange(_:with:) 方法,該方法用另一個集合替換當前集合指定范圍中的元素程剥。目標范圍和用來替換的集合的長度可以不同劝枣,其中任何一個都可以為空。協(xié)議使用這一方法提供在任意位置移除和插入元素的默認實現(xiàn)。

Set 也不是一個 RangeReplaceableCollection舔腾。與它不能遵守 MutableCollection 協(xié)議的原因相同:不滿足協(xié)議的語義溪胶。

文檔中對 replaceSubrange(_:with:) 方法的描述如下:從集合中刪除指定范圍的元素并在同一位置插入新元素。這與 “Set 在任意位置插入或移除元素都可以改變集合中元素的內(nèi)部位置”的情況不兼容稳诚。然而哗脖,即便 Set 以某種方式維持內(nèi)部元素順序的穩(wěn)定,RangeReplaceableCollection 協(xié)議的語義也不適合它扳还,因為協(xié)議定義的大多數(shù)方法對 Set 都沒有意義才避。例如,在 RangeReplaceableCollection 中 append(_:) 方法的意義是在集合的尾部插入一個新元素普办。Set 中相關(guān)操作的正確稱呼是 insert(_:) 工扎,因為你不能 append 任何元素到一個無序的集合中。

Dictionary

Dictionary 的故事與 Set 基本相同衔蹲。兩者都是基于哈希表實現(xiàn)的無序集合肢娘。(Dictionary 與 Set 是如此相似,它們在標準庫中共享同一個實現(xiàn)舆驶,使用 GYB 模板語言生成代碼橱健。你可以在 HashedCollections.swift.gyb 中找到相關(guān)資料)因此,由于相同的原因 Dictionary 不能遵守 MutableCollection 和 RangeReplaceableCollection沙廉。

另外一個方面是 Dictionary 獨有的拘荡。字典的元素類型 —— 即它把 Collection 指定的關(guān)聯(lián)類型 Iterator.Element 特化后的類型 —— 是一個 (Key, Value) 類型的元組:

struct Dictionary<Key: Hashable, Value>: Collection {
    typealias Element = (key: Key, value: Value)
    typealias Index = DictionaryIndex<Key, Value>

    subscript(position: Index) -> Element {
        ...
    }
    ...
}

這意味著在字典中所有從 Collection 和 Sequence 繼承來的 API 都會將 (Key, Value) 作為字典的元素。所以當你在 for 循環(huán)中遍歷一個 Dictionary 時撬陵,得到一個(Key珊皿,Value)。

Index 是一個完全獨立的類型巨税,并且與 Key 的類型沒有任何關(guān)聯(lián)蟋定。因此,你所熟悉的接受一個 Key 并返回一個 Optional<Value> 的下標并不是 Collection 協(xié)議所提供的下標草添。相反驶兜,它是直接在 Dictionary 上定義的,與 Collection 毫無關(guān)聯(lián)远寸。大多數(shù)情況下你會使用 Dictionary 特定的下標抄淑,但是 Collection 中的變體依舊存在:

let dict = ["Berlin": 1237, "New York": 1626]
dict["Berlin"] // returns Optional<Int>
for index in dict.indices {
    dict[index] // returns (key: String, value: Int) (not optional!)
}

如果字典從 MutableCollection 和/或RangeReplaceableCollection 中獲取方法,方法需要同時操作 Index 的值以及 (Key, Value) 驰后,即使從實現(xiàn)上來說可以做到兼容二者肆资,但是這種一致性可能不值得投入精力。

結(jié)論

Sequence 和 Collection 組成了 Swift 中集合類型的根基倡怎。而專門性的集合類型 BidirectionalCollection迅耘、RandomAccessCollection贱枣、MutableCollection 和 RandomAccessCollection 對你自定義的類型和算法的功能和性能特性提供了非常細粒度的控制。語義是決定類型是否應(yīng)該遵守這些協(xié)議中的一個或多個的重要因素颤专。

下一篇文章中纽哥,我將討論一個符合專門性集合協(xié)議上下文的有趣類型: String.CharacterView

本文由 SwiftGG 翻譯組翻譯栖秕,已經(jīng)獲得作者翻譯授權(quán)春塌,最新文章請訪問 http://swift.gg

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末簇捍,一起剝皮案震驚了整個濱河市只壳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌暑塑,老刑警劉巖吼句,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異事格,居然都是意外死亡惕艳,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門驹愚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來远搪,“玉大人,你說我怎么就攤上這事逢捺∷ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵劫瞳,是天一觀的道長倘潜。 經(jīng)常有香客問我,道長志于,這世上最難降的妖魔是什么窍荧? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮恨憎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘郊楣。我一直安慰自己憔恳,他們只是感情好,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布净蚤。 她就那樣靜靜地躺著钥组,像睡著了一般。 火紅的嫁衣襯著肌膚如雪今瀑。 梳的紋絲不亂的頭發(fā)上程梦,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天点把,我揣著相機與錄音,去河邊找鬼屿附。 笑死郎逃,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的挺份。 我是一名探鬼主播褒翰,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼匀泊!你這毒婦竟也來了优训?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤各聘,失蹤者是張志新(化名)和其女友劉穎揣非,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躲因,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡早敬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了毛仪。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片搁嗓。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖箱靴,靈堂內(nèi)的尸體忽然破棺而出腺逛,到底是詐尸還是另有隱情,我是刑警寧澤衡怀,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布棍矛,位于F島的核電站,受9級特大地震影響抛杨,放射性物質(zhì)發(fā)生泄漏够委。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一怖现、第九天 我趴在偏房一處隱蔽的房頂上張望茁帽。 院中可真熱鬧,春花似錦屈嗤、人聲如沸潘拨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铁追。三九已至,卻和暖如春茫船,著一層夾襖步出監(jiān)牢的瞬間琅束,已是汗流浹背扭屁。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留涩禀,地道東北人料滥。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像埋泵,于是被迫代替她去往敵國和親幔欧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容