c#更有效的比较两个集合的方法

本文关键字:两个 集合 方法 有效 比较 | 更新日期: 2023-09-27 18:01:53

我有两个集合

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

我不想使用foreach循环或其他方法,因为我认为应该有更好的方法。

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

  1. 在newCars中而不在currentCars中的汽车列表
  2. 不在newCars和currentCars中的汽车列表

类型Car有int属性Id。

有一个答案,它已经被删除说我所说的高效的意思是:更少的代码,更少的机制,更可读的情况

所以这样想,我有什么情况?

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

c#更有效的比较两个集合的方法

你可以这样做:

// 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。除了扩展方法(在。net 3.5及以上版本中可用)。

我相信这符合你的标准"更少的代码,更少的机制,更易读"

您可以使用Except:

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

但是这与foreach解决方案相比没有性能优势。它只是看起来更干净。
另外,请注意,您需要为Car类实现IEquatable<T>,因此比较是在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);

如果您在HashSet s中开始使用它们,则可以使用Except方法。

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

使用集合比使用列表要快得多。(在底层,列表只是做foreach,集合可以优化)。

您可以使用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方法:(

我将覆盖CarEquals以按id进行比较,然后您可以使用IEnumerable.Except扩展方法。如果你不能覆盖Equals,你可以创建你自己的IEqualityComparer<Car>来比较两辆车的id。

class CarComparer : IEqualityComparer<Car>
{
    public bool Equals(Car x, Car y)
    {
        return x != null && y != null && x.Id == y.Id;
    }
    public int GetHashCode(Car obj)
    {
        return obj == null ? 0 : obj.Id;
    }
}

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

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

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

这是您在不了解更多列表的情况下所能做的最好的事情(例如,如果列表已排序,您可能能够做得稍微好一点,但是,由于您必须至少"触摸"每辆车一次以检查它是否在新车列表中,因此您永远无法做得比O(N)更好)

如果Id属性的比较足以说明一辆车是否等于另一辆车,为了避免某种循环,您可以用自己的类覆盖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 ....
}

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