使用C#从另一个列表中排除一个列表的项目

时间:2012-05-10 08:52:15

标签: c# winforms list optimization

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

  • 如果列表中的副本具有“偶数”索引 - 我需要在它之后删除THIS元素和NEXT元素。
  • 如果列表中的副本有一个“奇数”索引 - 我需要删除这个元素和一个元素BEFORE **。
  • 列表中可能有许多相同副本的出现。即一个可能带有“奇数”索引,另一个可能带有“偶数”。

因为我自己创建了一个解决方案,所以我不会要求解决方案。然而,在多次执行此方法之后 - “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;
                }
            }
        }
    }
}

其中:

  • exclude - 仅包含重复项的列表。此列表最多可包含30个元素。
  • listcopy - 应检查重复项的列表。如果找到“排除”的副本 - &gt;执行删除操作。此列表最多可包含2000个元素。

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

4 个答案:

答案 0 :(得分:5)

更快的方式(O(n))将执行以下操作:

  1. 浏览exclude列表并将其转换为HashSetO(n)
  2. 在检查中,检查正在测试的元素是否在集合中(再次O(n)),因为HashSet中的状态测试是O(1)
  3. 也许您甚至可以更改算法,以便exclude集合从一开始就是HashSet,这样您就可以省略步骤1并获得更快的速度。

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


    编辑:
    另一个想法如下:您可能正在创建一些列表的副本并使此方法修改它? (根据参数名称猜测。)然后,您可以将其更改为以下内容:将原始数组传递给方法,并使方法分配新数组并返回它(您的方法签名应该比private List<int> getWithoutDoubles(HashSet<int> exclude, List<int> original))更像。


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

    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

答案 1 :(得分:2)

撤消循环,使其从.Count - 1开始,然后转到0,这样您就不必在其中一个案例中更改iCount仅评估一次每个系列。

答案 2 :(得分: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 :(得分:1)

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