【问题标题】:ES6: Is it dangerous to delete elements from Set/Map during Set/Map iteration?ES6:在 Set/Map 迭代期间从 Set/Map 中删除元素是否危险?
【发布时间】:2016-03-11 12:23:06
【问题描述】:

new Set() 的安全代码可能如下所示:

let items = [];
for (let item of set)
  if (isBad(item))
    items.push(item);
for (let item of items)
  set.delete(item)

我可以将代码简化为:

for (let item of set)
  if (isBad(item))
    set.delete(item);

new Map() 的安全代码可能如下所示:

let keys = [];
for (let [key, val] of map)
  if (isBadKey(key) || isBadValue(val))
    keys.push(key);
for (let key of keys)
  map.delete(key)

我可以将代码简化为:

for (let [key, val] of map)
  if (isBadKey(key) || isBadValue(val))
    map.delete(key)

【问题讨论】:

    标签: javascript ecmascript-6


    【解决方案1】:

    是的,您可以简化为,这是完全安全的。

    • Set 和 Map 始终按插入顺序迭代
    • 删除项目不会影响任何迭代器的位置 - 您可以看到集合的形状没有改变,只是被清空。
    • 所以:已删除且尚未迭代的元素将不会被迭代
    • 已经迭代并被删除的元素(如您的情况)不会影响其他迭代/查找。
    • 在迭代期间添加的元素(并且还不是集合的一部分)将始终被迭代

    从最后一点可以看出,唯一危险的事情就是

    const s = new Set([1]);
    for (let x of s) {
        s.delete(x);
        s.add(1);
    }
    

    但不是因为未定义的行为或内存积累,而是因为无限循环。

    【讨论】:

    • 能否请您添加一个链接到可证实您的陈述的参考文档?
    • @jtbandes 我不知道有任何文档可以准确说明这一点(但是您可能想尝试 MDN),我的答案中的结论直接遵循 observable semantics of collections in the spec 作为条目列表。
    • Map#forEach 规范下有一条注释说类似的话。
    • 您应该说“是的,它很安全”,而不仅仅是“是”,因为标题以“它是否危险...”开头。
    【解决方案2】:

    我会说是的,它是安全的。当您在后台使用for ... of 遍历Set/Map 时,循环将通过@@iterator。而Iterator 仅与.next() 一起使用:所以没有索引,也不管当前位置之前是什么。只有下一个元素很重要。

    因此,除非您删除当前迭代器位置“前面”的元素 - 这样做是安全的。

    【讨论】:

    • 如果Set() 实现为二叉树会怎样?删除节点可能会导致树重新平衡。它会损坏迭代器操作吗?
    • 不是。根据规范 - 内部Set 更像是ListSet set's [[SetData]] internal slot to a new empty List.
    • 规范只定义了必需的行为,而不是它应该如何实现。实现可以 - 例如 - 通过将其实现为具有插入顺序链接集条目的哈希集来实现定义的行为,从而在保持可预测顺序的同时实现分期 O(1) 查找。
    • @gavenkoa 关于删除 - 这是O(size)
    • @Bergi,根据规范 - Yes: Repeat for each e that is an element of entries, - 所以一个循环遍历所有条目。但是,当然,只有按照规范实施。
    猜你喜欢
    • 1970-01-01
    • 2015-12-05
    • 1970-01-01
    • 2015-11-30
    • 2011-02-21
    • 2014-12-30
    • 2015-12-19
    • 2014-01-04
    相关资源
    最近更新 更多