私はいつも考えていました。「なぜあなたはあなた自身のコピーオンライトを書く必要があるのですか?構造があり、それらがあなたのためにすべてをするほど速いのはクールです。」
しかし、あなたがあなた自身のタイプを書き始めて、あなたがLinkedListsに夢中になるまで、これはすべて必要ではありません。
このリンクされたリストとは何ですか?その利点は何ですか?
リンクリストには、アレイなどの連続したストレージオプションに比べていくつかの理論上の利点があります。
リンクされたリストには、最初の要素を挿入するためのO(1)時間の複雑さがあります。配列の時間の複雑さはO(n)です。
シーケンスやコレクションなどのSwiftコレクションプロトコルへの準拠は、かなり少数の要件に対して多くの便利な方法を提供します。
リンクリストは、各アイテムがノードと呼ばれる一連のデータアイテムです。
リンクリストには主に2つのタイプがあります。
単一リンクリストは、各ノードが次のノードへのリンクのみを持つリンクリストです。
二重リンクリストは、各ノードが前のノードと次のノードへのリンクを持つリンクリストです。
, . , head tail.
:
public class Node<Value> {
public var value: Value
public var next: Node?
public init(value: Value, next: Node? = nil) {
self.value = value
self.next = next
}
}
public struct LinkedList<Value> {
public var head: Node<Value>?
public var tail: Node<Value>?
public init() {}
public var isEmpty: Bool { head == nil }
}
, ! , . . .
Node'a , reference type. , . .
? LinkedList . COW .
value type COW . , ( ) .
private mutating func copyNodes() {
guard var oldNode = head else {
return
}
head = Node(value: oldNode.value)
var newNode = head
while let nextOldNode = oldNode.next {
newNode!.next = Node(value: nextOldNode.value)
newNode = newNode!.next
oldNode = nextOldNode
}
tail = newNode
}
. , O (n) …
COW
O (n) .
, . - , .
Swift isKnownUniquelyReferenced. , , .
また、copyNodes()関数の先頭にコードを追加する場合は、さらにコピーする必要はありません。
private mutating func copyNodes(returningCopyOf node:
Node<Value>?) -> Node<Value>? {
guard !isKnownUniquelyReferenced(&head) else {
return nil
}
guard var oldNode = head else {
return nil
}
head = Node(value: oldNode.value)
var newNode = head
var nodeCopy: Node<Value>?
while let nextOldNode = oldNode.next {
if oldNode === node {
nodeCopy = newNode
}
newNode!.next = Node(value: nextOldNode.value)
newNode = newNode!.next
oldNode = nextOldNode
}
return nodeCopy
}
この方法には、以前の実装と多くの共通点があります。主な違いは、渡されたパラメーターに基づいて、新しくコピーされたノードを返すことです。
したがって、Copy-on-writeは、遠いものではなく、内部的なものでもありません。そして、非常に扱いやすく、理解しやすいです。