【问题标题】:exclude items of one list from another with C#使用 C# 从另一个列表中排除一个列表的项目
【发布时间】:2015-08-30 02:52:40
【问题描述】:

关于如何从另一个列表中排除一个列表中的项目,我有一个相当具体的问题。常见的方法,例如 except() 不会这样做,原因如下:

  • 如果列表中的重复项具有“偶数”索引 - 我需要删除 THIS 元素和它之后的 NEXT 元素。
  • 如果列表中的重复项具有“奇数”索引 - 我需要删除此元素和一个元素之前**它。
  • 在一个列表中可能会出现多次相同的重复项。即一个可能是“奇数”索引,另一个可能是“偶数”。

我不是在寻求解决方案,因为我自己创建了一个。然而,在多次执行此方法后 - “ANTS 性能分析器”显示该方法经过了整个执行时间的 75%(40 秒中的 30 秒)。问题是:是否有更快的方法来执行相同的操作?我已经尝试优化我当前的代码,但它仍然缺乏性能。这里是:

private void removedoubles(List<int> exclude, List<int> listcopy)
{
    for (int j = 0; j < exclude.Count(); j++)
    {
        for (int i = 0; i < listcopy.Count(); i++)
        {
            if (listcopy[i] == exclude[j])
            {
                if (i % 2 == 0) // even
                {
                    //listcopy.RemoveRange(i, i + 1);
                    listcopy.RemoveAt(i);
                    listcopy.RemoveAt(i);
                    i = i - 1;
                }
                else //odd
                {
                    //listcopy.RemoveRange(i - 1, i);
                    listcopy.RemoveAt(i - 1);
                    listcopy.RemoveAt(i - 1);
                    i = i - 2;
                }
            }
        }
    }
}

地点:

  • 排除 - 仅包含重复项的列表。此列表最多可包含 30 个元素。
  • listcopy - 应检查重复的列表。如果发现“排除”中的重复项 -> 执行删除操作。此列表最多可包含 2000 个元素。

我认为 LINQ 可能会有所帮助,但我不太了解它的语法。

【问题讨论】:

  • 粗体和大写,在我看来太多尖叫了。
  • 它是为了展示主要的想法。无论如何,编辑问题
  • AHHH - 永远不要永远在 for 循环中更改循环变量!请改用 while 循环!
  • 您正在调用 Count() 方法,它是 LINQ 的一部分。这可能比简单地使用 Count 属性要慢,该属性已经内置在 List 类中。

标签: c# winforms list optimization


【解决方案1】:

一种更快的方法 (O(n)) 是执行以下操作:

  1. 遍历exclude 列表并将其变为HashSet (O(n))
  2. 在检查中,检查被测试的元素是否在集合中(同样是O(n)),因为在HashSet 中的存在测试是O(1)。

也许您甚至可以更改您的算法,使 exclude 集合从一开始就是 HashSet,这样您就可以省略第 1 步并获得更快的速度。

(您当前的方式是O(n^2)。)


编辑:
另一个想法如下:您可能正在创建某个列表的副本并让此方法对其进行修改? (根据参数名称猜测。)然后,您可以将其更改为:将 original 数组传递给方法,并让方法分配新数组并返回它(您的方法签名应该比 private List&lt;int&gt; getWithoutDoubles(HashSet&lt;int&gt; exclude, List&lt;int&gt; original)) 之类的要好。


编辑:
如果您按以下方式重新组织输入数据可能会更快:由于项目总是成对删除(偶数索引+以下奇数索引),您应该提前将它们配对!这样你的列表 if ints 就变成了成对的 int 列表。这样你的方法可能是这样的:

private List<Tuple<int, int>> getWithoutDoubles(
              HashSet<int> exclude, List<Tuple<int, int>> original)
{
    return original.Where(xy => (!exclude.Contains(xy.Item1) &&
                                 !exclude.Contains(xy.Item2)))
           .ToList();
}

(删除第一个或第二个项目在排除集合中的对)。而不是Tuple,也许您可​​以将项目打包到您的自定义类型中。

【讨论】:

  • 这也很棒,因为它只需要一个循环并自动从“要删除”列表中删除双打。
【解决方案2】:

这是获得结果的另一种方法。

var a = new List<int> {1, 2, 3, 4, 5};

var b = new List<int> {1, 2, 3};

var c = (from i in a let found = b.Any(j => j == i) where !found select i).ToList();

c 将包含 4,5

【讨论】:

    【解决方案3】:

    反转您的循环,使它们从.Count - 1 开始并转到0,因此您不必在其中一种情况下更改i,并且Count 在每个集合中只评估一次。

    【讨论】:

    • 好主意! *去编码
    • 他没有打电话给Count,他打电话给Count()。第一个只是List 类的属性,另一个是LINQ - 我想,首先将代码更改为使用属性而不是方法将是一个很大的改进。
    • @Alex:请注意,如果您倒数,则需要更改要删除的项目的索引计算。
    【解决方案4】:

    您可以将 List 转换为 LinkedList 并尝试一下吗? List.RemoveAt() 比 LinkedList.Remove() 更昂贵。

    【讨论】:

      猜你喜欢
      • 2011-02-14
      • 1970-01-01
      • 1970-01-01
      • 2012-12-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多