【问题标题】:c# ListView.Items[i].remove is very slowc# ListView.Items[i].remove 很慢
【发布时间】:2013-05-27 12:15:26
【问题描述】:

这是我第一次来这里,我正在努力解决这个问题。 我有这段代码:

try
{
    progressBar1.Maximum = lista.Items.Count;
    lista.BeginUpdate();

    for (int i = 0; lista.Items.Count > i; i++)

    //for (int i = lista.Items.Count - 1; -1 < i; i--)
    {
        if (lista.Items[i].SubItems[1].Text.ToLower().Contains(Text) == false)
        {                        
            lista.Items[i].Remove();                        
        }

        progressBar1.Value = progressBar1.Value + 1;
    }

    lista.EndUpdate();

    progressBar1.Value = 0;
}
catch (Exception errore)
{
    txt_info.Text = "" + errore.Message;
    progressBar1.Value = 0;
}

lista.items[i].remove 方法非常慢。 listaListView,我正在处理大于 50,000 行的日志文件。 有没有办法加快这个过程?

【问题讨论】:

  • 很好奇...lista.Items.RemoveAt(i) 的速度是否不同?也许(与直觉相反)该类必须返回并自行解析索引。
  • 您不应在 For 循环中更改数据结构的大小(从中删除项目)。尝试在没有删除的情况下重新编写循环。例如标记需要在 for 循环中删除的索引,然后将它们删除。
  • @RezaShirazian One 显然不能用foreach 做到这一点...我相信使用for 删除项目是合理的(最终还是有一些代码必须这样做) -显然也写错了(如上面的示例,由于跳过了刚刚删除的项目旁边的项目)是个坏主意。
  • DonBoitnott 是对的。 RemoveAt(i) 是答案 - 已验证。是的,循环中的逻辑没有意义。

标签: c# listview


【解决方案1】:

我会采取不同的方法并使用 LINQ,如下所示:

lista.Items = lista.Items.Where(x=>x.SubItems[1].Text.ToLower.Contains(Text)).AsParallel().ToList();

基本上,重建列表一次,而不是一次又一次地尝试删除单个项目。

【讨论】:

  • 这是一个很好且紧凑的实现,尽管 AsParallel 让我觉得有点矫枉过正。
  • ToLower 和 Contains 都是 CPU 开销操作,如果列表很大,它将受益于 Parallel 方法
  • ListView.Items 是只读属性,所以这不起作用。
  • 您应该将.ToLower().Contains(Text) 替换为.IndexOf(Text, StringComparison.CurrentCultureIgnoreCase) != -1,这样可以避免创建新的字符串进行比较。
  • @MichaelLiu 哇,好电话,我什至没有检查它是否有二传手。
【解决方案2】:

最简单的选择是使用列表自己的RemoveAll method

list.RemoveAll(x =&gt; !x.SubItems[1].Text.ToLower().Contains(Text))

附言

您可能希望在实际比较中寻找速度增益。 如果您的要求符合要求,使用 String.Compare 会快得多。如果您想检查子字符串,我建议使用ToUpperInvariant 处理与不变性相关的问题 - it's designed to be faster

【讨论】:

  • ListViewItemCollection 没有 RemoveAll 方法,所以这不起作用。
  • @Yogee 我建议过滤速度要快几倍以进行直接比较 - 而不是 Contains 的替代品。如果他想用Contains,那么ToUpperInvariant更快。
  • @Yogee SO 也可以作为一般建议的地方。不仅仅是“给我密码”。尽管如此,我还是对其进行了编辑以消除歧义。
  • 感谢 Asti 编辑答案。我将删除我过时的评论。顺便说一句,我在这个线程中发布的代码相当快。当我看到我的列表时,我不确定它是否会对性能产生任何影响。RemoveAll 对于 50K 记录来说很快。在这种情况下,瓶颈是直接操作(如编辑/删除)列表视图项。我注意到如果你清除和添加元素(尤其是使用 AddRange),它会快得多。
【解决方案3】:

您可以将它放在后台工作人员中,并让它自己执行此操作。因此,在此过程发生时,您的用户仍然可以使用该程序。

【讨论】:

    【解决方案4】:
    ListViewItem[] allElements = new ListViewItem[listView1.Items.Count];
    listView1.Items.CopyTo(allElements, 0);
    List < ListViewItem > list = allElements.ToList();
    list.RemoveAll(item => item.SubItems[1].Text.ToLower().Contains(TextToFind) == false);
    listView1.BeginUpdate();
    listView1.Clear();
    listView1.Items.AddRange(list.ToArray());
    listView1.EndUpdate();
    

    第一条规则永远不会在 for 循环中更新列表。您的逻辑只会运行到列表的一半。我想这不是你想要的。
    我已经看到即使使用 BeginUpdate 和 EndUpdate 操作 listview.items 也很慢。关键是在外部进行操作(在列表中左右),然后用 AddRange 重新填充列表(这比 Add 快得多)。

    【讨论】:

    • 我已经尝试了上面的代码,但给了我一个空列表。我想念什么吗?在您的评论中,您说您正在使用 foreach 循环,但我在您留下的代码中没有看到它。
    • @user2441083:我以前的评论是使用 foreach,然后我从列表中删除元素。但后来我使用了不需要 foreach 的 RemoveAll。顺便说一句,列表什么时候变空?全部删除后?如果是这样,你需要检查“RemoveAll”中写的条件是否可能这样做。
    • @user2441083:我在我的应用程序中编写了相同的逻辑,它运行良好且足够快.. 删除 50,000 个二维字符串条目的 10% 的列表时间大约需要 2450 毫秒,如 LIstViewItems listView1.Items。您确定要检查 item.SubItems[1] 而不是 item.SubItems[0]?
    • 我做了一些调试,问题实际上是在 lista.clear() 之后。似乎 AddRange 没有填满列表。
    • 完成!!!它工作得很好,我刚刚阅读了列 lista.Columns.Add("Description");在使用 AddRange 之前。非常感谢您的支持!!!
    【解决方案5】:

    如果你在 for 循环中删除一个项目,你应该将计数器设置为少 1,这样你就不会错过一个。因为您删除了 [i],所以 [old i+1] 变成了 [new i](Items.Count 也减 1)并且您将错过检查 [new i]。

    例如: ListView.Items.Remove[i]; i--;

    【讨论】:

      【解决方案6】:

      动态访问列表很慢,更适合迭代。我建议在列表视图之外操作数据(也许通过在遍历源集合时将要显示的行一次复制到一个临时列表中),然后在最后分配给列表视图。这可以在不同的线程上完成(以提高 UI 性能)并且不需要昂贵的查找。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-21
        • 2015-01-18
        • 1970-01-01
        • 1970-01-01
        • 2012-05-06
        • 2015-11-01
        • 2018-12-31
        相关资源
        最近更新 更多