c#更有效的比较两个集合的方法
本文关键字:两个 集合 方法 有效 比较 | 更新日期: 2023-09-27 18:01:53
我有两个集合
List<Car> currentCars = GetCurrentCars();
List<Car> newCars = GetNewCars();
我不想使用foreach循环或其他方法,因为我认为应该有更好的方法。
我正在寻找更有效的方法来比较这些集合并获得结果:
- 在newCars中而不在currentCars中的汽车列表
- 不在newCars和currentCars中的汽车列表
类型Car有int属性Id。
有一个答案,它已经被删除说我所说的高效的意思是:更少的代码,更少的机制,更可读的情况
所以这样想,我有什么情况?
什么是更少的代码,更少的机制,更可读的案例?
你可以这样做:
// 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方法:(
我将覆盖Car
的Equals
以按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…