C#比较两个集合的更有效方式

时间:2011-07-13 14:26:12

标签: c# .net c#-4.0

我有两个收藏品

List<Car> currentCars = GetCurrentCars();
List<Car> newCars = GetNewCars();

我不想使用foreach循环或其他东西,因为我认为应该有更好的方法来做到这一点。

我正在寻找更有效的方法来比较这些集合并获得结果:

  1. 在newCars中而不在currentCars中的汽车列表
  2. 不在newCars和currentCars中的汽车列表
  3. 类型Car具有int属性Id。

    有一个答案,已经删除了说 我的意思是说效率更高:更少的代码,更少的机制和更具可读性的案例

    所以这样思考我的情况是什么?

    什么是更少的代码,更少的机制,更可读的案例?

8 个答案:

答案 0 :(得分:13)

你可以这样做:

// 1) List of cars in newCars and not in currentCars
var newButNotCurrentCars = newCars.Except(currentCars);

// 2) List of cars in currentCars and not in newCars
var currentButNotNewCars = currentCars.Except(newCars);

该代码使用Enumerable.Except扩展方法(在.Net 3.5及更高版本中提供)。

我相信这符合您的“更少代码,更少机制和更易读”的标准。

答案 1 :(得分:11)

您可以使用Except

var currentCarsNotInNewCars = currentCars.Except(newCars);
var newCarsNotInCurrentCars = newCars.Except(currentCars);

但是这比foreach解决方案没有性能优势。它看起来更干净 另外,请注意,您需要为IEquatable<T>类实现Car,因此需要对ID进行比较,而不是在引用上进行比较。

从绩效方面来说,更好的方法是不使用List<T>而使用Dictionary<TKey, TValue>并将ID作为关键字:

var currentCarsDictionary = currentCars.ToDictionary(x => x.ID);
var newCarsDictionary = newCars.ToDictionary(x => x.ID);

var currentCarsNotInNewCars = 
    currentCarsDictionary.Where(x => !newCarsDictionary.ContainsKey(x.Key))
                         .Select(x => x.Value);

var newCarsNotInCurrentCars = 
    newCarsDictionary.Where(x => !currentCarsDictionary.ContainsKey(x.Key))
                     .Select(x => x.Value);

答案 2 :(得分:3)

如果您从HashSet开始,可以使用Except方法。

HashSet<Car> currentCars = GetCurrentCars();
HashSet<Car> newCars = GetNewCars();

currentCars.Except(newCars);
newCars.Except(currentCars);

w / set比列表快得多。 (在引擎盖下,列表只是做一个foreach,可以优化集合。)

答案 3 :(得分:2)

我会覆盖Equals的{​​{1}}以按ID进行比较,然后您可以使用Car扩展方法。如果您无法覆盖IEnumerable.Except,则可以创建自己的Equals,按ID对比两辆车。

IEqualityComparer<Car>

答案 4 :(得分:2)

您可以使用LINQ ...

        List<Car> currentCars = new List<Car>();
        List<Car> newCars = new List<Car>();

        List<Car> currentButNotNew = currentCars.Where(c => !newCars.Contains(c)).ToList();
        List<Car> newButNotCurrent = newCars.Where(c => !currentCars.Contains(c)).ToList();

......但不要被愚弄。它可能对你来说代码较少,但肯定会有某些for循环

编辑:没有意识到有一种Except方法:(

答案 5 :(得分:1)

如果您正在寻找效率,请在Cars上实施IComparable(对您的唯一ID进行排序)并使用SortedList。然后,您可以一起浏览您的收藏,并在O(n)中评估您的支票。当然,这会增加List插入的成本,以维持排序的性质。

答案 6 :(得分:0)

您可以将较小的列表复制到基于哈希表的集合(如HashSet或Dictionary)中,然后迭代第二个列表并检查该项是否存在于哈希表中。

这将减少从foreach案例中的朴素foreach中的O(N ^ 2)到O(N)的时间。

这是您在不了解更多关于列表的情况下可以做的最好的事情(例如,如果对列表进行排序,您可以更好地执行 little ,但是,因为您必须“触摸”每辆车至少检查一次,看看它是否在新车清单上,你永远做不到O(N)

答案 7 :(得分:0)

如果比较Id属性就足以说明Car是否等于另一个,为了避免某种循环,你可以用自己的类覆盖List,跟踪项目并使用整个集合上的IEqualityComparer,如下所示:

class CarComparer : IList<Car>, IEquatable<CarComparer>
{
    public bool Equals(CarComparer other)
    {
        return object.Equals(GetHashCode(),other.GetHashCode());
    }

    public override int GetHashCode()
    {
        return _runningHash;
    }

    public void Insert(int index, Car item)
    {
        // Update _runningHash here
        throw new NotImplementedException();
    }

    public void RemoveAt(int index)
    {
        // Update _runningHash here
        throw new NotImplementedException();
    }

    // More IList<Car> Overrides ....
}

然后,您只需要覆盖AddRemove等以及可能影响列表中项目的任何其他方法。然后,您可以保留一个私有变量,该变量是列表中某些项目的哈希值。覆盖Equals方法时,您可以只比较这个私有变量。到目前为止,这不是最干净的方法(因为你必须跟上你的哈希变量),但这将导致你不必循环进行比较。如果是我,我会像其他人在这里提到的那样使用Linq ......