【问题标题】:When to loop from 0 to end and when to loop from end to 0?什么时候从 0 循环到 end,什么时候从 end 循环到 0?
【发布时间】:2015-05-14 02:16:38
【问题描述】:

从 0 循环到列表末尾(列表或映射)的场景有哪些,从末尾循环到 0 的场景有哪些?这是代码 sn-p 从 0 到 end 遍历列表以过滤掉一些特定的学生姓名。此代码在任何情况下都会引发任何异常吗?从列表末尾迭代到零对这段代码有什么影响吗?

List<Dictionary<string, string>> allStudentsList = allStudentsArray.ToList();
for (int i = 0; i < allStudentsList .Count-1; i++) {
    Dictionary<string, string> student= allStudentsList .ElementAt(i);
    string studentName;
    bool hasValue = studentName.TryGetValue("id", out studentName);
    if (hasValue) {
        if (studentName.StartsWith("John") {
            allStudentsList.RemoveAt(i);
            i--;
        }
    }
}

【问题讨论】:

  • 将此作为评论,因为它与您的问题无关,但您应该将 elementat(i) 替换为简单地在列表中建立索引。 ElementAt 是一个 linq 运算符,因此它要么针对列表进行了优化(并且您只添加了一个方法开销)要么它没有特定于列表的路径,然后它会导致巨大的减速,因为它需要每次迭代整个列表直到查找元素的索引。

标签: c# for-loop


【解决方案1】:

首先,您的问题包含语义错误。它是:

end-10(包括0)。

如果您以end 开头,由于ElementAt 方法规范,您将立即获得ArgumentOutOfRangeException

如果您从从右到左移动,则无需执行递增 (i++),以防hasValue 为真。仅仅因为索引左侧的项目没有移动。所以你可以将它实现为:

List<Dictionary<string, string>> allStudentsList = allStudentsArray.ToList();
for (int i = allStudentsList.Count-1; i >= 0; i--) {
    Dictionary<string, string> student = allStudentsList.ElementAt(i);
    string studentName;
    bool hasValue = studentName.TryGetValue("id", out studentName);
    if (hasValue) {
        if (studentName.StartsWith("John") {
            allStudentsList.RemoveAt(i);
        }
    }
}

接下来,它可以对性能产生影响。如果从List&lt;T&gt;.RemoveAt 中删除一个元素,则意味着索引右侧的所有元素都被放置在左侧一个位置。现在如果你先从右边删除,右边的项目数会很少,而且如果索引接近零,要向左移动的项目数会更少(仅仅是因为一些元素已经删除)。但是,如果 hasValue 很少是 true,请不要指望性能会大幅提升。

在这种情况下,for 是向前还是向后都无关紧要,因为不同迭代之间存在“共享状态”。

【讨论】:

  • 我不同意你的前两段,你说 OP 的“问题包含语义错误”。 OP 使用单词“end”来描述列表的结尾,而不是作为索引变量。具体来说,OP 两次说“列表结尾”,所以很明显他(或她)没有使用“结束”作为索引。所以说它应该是“end - 1”就像是在说“这列火车到了轨道的尽头 - 1”。
【解决方案2】:

现在,我想为 CommuSoft 之前最出色、最彻底的评论提供一个伴侣答案。 (即“不相关”,而是:“平行 .. 相切 .. 'FYI' ...”)*

一般(以及不管(!)所使用的语言...),如果您通过any按索引迭代> 您要从中删除事物的某种数据结构,您总是希望“从头到尾”迭代。 (否则,真的无所谓……)

为什么?因为,每当您从任何类型的集合中删除任何内容时,该集合的“长度”都会减少 1。如果您是从后向前迭代,这不是问题:您永远不会“错过任何东西”,您也不会“遇到两次相同的元素”。如果您以相反的方式旅行,这两者(!)都是非常真实的(因此,“完全可以避免”)问题。

【讨论】:

  • 小评论:不完全正确,对于链表,向后迭代会导致O(n^2),而向前迭代会产生O(n) 时间复杂度。一些迭代器/枚举器提供删除功能,在这种情况下,可以进行内联删除......
  • 我不完全确定你的结论是什么逻辑“向前迭代链表 vs. 向后迭代”会在 O-cost 上产生任何形式的差异。 (尽管如此,我很客气地承认你的观点,没有进一步评论,“毫无疑问,真实的”。)事实上,我的 cmets 专门针对那些“一个人在收藏中的当前位置”的情况" 由 序数表示, 而不是(严格来说是“链表”的情况)“指针”。因此,我认为我们各自的有效 cmets 之间没有冲突。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
  • 2021-03-23
  • 2011-11-12
  • 2014-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多