В C# более эффективный способ сравнения двух коллекций
У меня две коллекции
List<Car> currentCars = GetCurrentCars();
List<Car> newCars = GetNewCars();
Я не хочу использовать цикл foreach или что-то еще, потому что я думаю, что должен быть гораздо лучший способ сделать это.
Я ищу более эффективный способ сравнить эти коллекции и получить результаты:
- список автомобилей, которые находятся в newCars, а не в currentCars
- список автомобилей, которые не находятся в newCars и в currentCars
тип автомобиля имеет свойство int Идентификатор.
был ответ, который уже удален, говоря Что я имею в виду, говоря эффективно: меньше кода, меньше механики и более читаемые случаи
Итак, думая таким образом, какие у меня есть случаи?
что было бы меньше кода, меньше механики и более читаемых случаев?
8 ответов
можно использовать Except
:
var currentCarsNotInNewCars = currentCars.Except(newCars);
var newCarsNotInCurrentCars = newCars.Except(currentCars);
но это не имеет никакого преимущества над foreach
решение. Просто выглядит чище.
Кроме того, имейте в виду тот факт, что вам нужно реализовать IEquatable<T>
для Car
класс, поэтому сравнение выполняется по идентификатору, а не по ссылке.
Performancewise, лучшим подходом было бы не использовать 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);
вы можете сделать это так:
// 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);
код использует перечисли.Кроме метод расширения (доступен в .Net 3.5 и более).
Я считаю, что это соответствует вашим критериям "меньше кода, меньше механики и более читаемый".
Если вы начнете с них в 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();
...но не обманывайтесь. Это может быть меньше кода для вас, но там определенно будут некоторые для циклов где-то
EDIT: не понял, что есть метод Except: (
Я бы переопределить Equals
на Car
для сравнения по 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;
}
}
Если вы ищете эффективность, реализуйте IComparable на автомобилях (сортировка по вашему уникальному идентификатору) и используйте SortedList. Затем вы можете пройти через свои коллекции вместе и оценить свои чеки в O(n). Это, конечно, поставляется с дополнительной стоимостью для списка вставок для поддержания отсортированного характера.
вы можете скопировать меньший список в коллекцию на основе хэш-таблицы, такую как HashSet или Dictionary, а затем повторить второй список и проверить, существует ли элемент в хэш-таблице.
Это уменьшит время от O(N^2) в наивном foreach внутри случая foreach до O (N).
Это лучшее, что вы можете сделать, не зная больше о списках (вы можете сделать мало лучше, если списки отсортированы, например, но, так как вы должны " коснуться" каждый автомобиль хотя бы один раз, чтобы проверить, есть ли он в новом списке автомобилей, вы никогда не сможете сделать лучше, чем O(N))
если сравнение свойства Id будет достаточно, чтобы сказать, если автомобиль равен другому, чтобы избежать какого-то цикла, вы можете переопределить список своим собственным классом, который отслеживает элементы и использует 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
и т. д. и любые другие методы, которые могут повлиять на элементы в списке. Затем вы можете сохранить закрытую переменную, которая является хэшем какой-то Идентификаторы элементов в списке. Когда переопределение Equals
методы вы можете просто сравнить эту частную переменную. Не самый чистый подход (поскольку вы должны идти в ногу со своей хэш-переменной), но это приведет к тому, что вам не придется делать цикл для сравнения. Если бы это был я,Я бы просто использовал Linq, как некоторые упоминали здесь...