我知道,从Array
数据结构中删除和删除成员是不安全的。
var datas = ["X", "Y", "Z", "1", "2", "3"]
for (index, data) in datas.enumerated() {
if data == "Y" || data == "Z" || data == "3" {
datas.remove(at: index) // Crash!
}
}
字符串
但是,Set
数据结构呢?我在运行以下代码时没有遇到运行时错误。
var datas = Set(["X", "Y", "Z", "1", "2", "3"])
for data in datas {
if data == "Y" || data == "Z" || data == "3" {
datas.remove(data) // No crash.
}
}
型
但这样做安全吗?会出现什么问题呢?
4条答案
按热度按时间mklgxw1f1#
迭代任何序列(包括数组和集合)都可以使用一个名为
next()
的函数。在next()
值可用之前,您可以进行任何操作。因此,**可以从正在迭代的数组中删除元素。不安全的部分是调用index!,因为从数组中删除一个项可能会导致索引的更改。
尽管你可以小心地执行安全的基于索引的操作:
字符串
回顾
***在迭代过程中,修改任何序列的元素(包括
Array
和Set
)都是安全的。lmyy7pcs2#
这是关于数据结构的。
1.数组结构是一个有序的集合,允许你通过位置(索引)来访问它的元素。在缺少索引的地方访问会导致致命错误。
key
访问它的元素。它是一个幕后的哈希表。这就是为什么所有的元素都应该符合Hashable
协议。当获取/删除一个元素时,Set首先用hash函数从哈希表中查找,然后返回值,如果它存在。有时会有共谋,但是Set结构中的remove
函数返回一个可选值,所以它根本不会崩溃。8fq7wneg3#
这两个版本的代码并没有真正的可比性。你在这里有点像在比较苹果和橘子。
在数组的代码中,你正在使用索引 * 删除元素。当你删除一个元素时,数组中其他元素的索引可能会改变,一些曾经有效的索引可能会变得无效。循环最终到达“3”,它在原始数组中的索引为5,但现在不再有效,因为数组现在只有4个元素。
注意数组代码中的崩溃并不是由于在迭代数组时从数组中删除元素而引起的。崩溃仅仅是因为你使用了无效的索引。正如你将在后面看到的,如果你不使用索引,在迭代数组时从数组中删除元素是完全可以的。
在sets的代码中,没有索引。你只是使用了
Set.remove(_ member: Element)
方法。你永远不能传递一个“无效”的元素给这个方法-要么元素存在于set中,它将被删除,要么元素不存在,什么也不会发生。所以回答你的问题:你的sets代码是安全的。为了实际比较数组和集合的行为,你应该使用一个索引来从集合中删除一个元素:
字符串
现在代码崩溃了(虽然我不确定这是否会 * 总是 * 发生)。
或者,你也可以在从数组中删除时不使用索引:
型
这段代码不会崩溃。
enumerated
返回的元组序列中的第一个元素实际上并不是该元素在集合中的索引。它只是一个从0到-1的数字。碰巧它与Array
的每个元素的索引相同。xzlaal3s4#
在这两种情况下,手动使用
for
循环进行迭代都不是好方法。对于Array,您可以使用简单的
removeAll(where:)
:字符串
对于Set,你也可以使用
removeAll
,但是集合减法(例如subtracting(_:)
,或者与subtract(_:)
一起使用)会更习惯:型