Предположим, что имеются две коллекции объектов. Я хочу получить объекты в первой коллекции, которые не содержатся во второй коллекции.Linq - лучше `Enumerable.Except()` оператор (производительность и гибкость)?
Для коллекций примитивных типов, которые легко:
new[]{1,2,3,4}.Except(new[]{2,3}); // => {1, 4}
Но что, если я хочу использовать более сложную структуру? В приведенном ниже примере я хочу сравнить поле Id
.
class Person { string Name; int Id ; }
var lst1 = new[]{ new Person("Ann", 1), new Person("Bob", 2) };
var lst2 = new[]{ new Person("Cathy", 3), new Person("Bob", 2) };
Ну, по общему мнению, кажется, предлагает эти два варианта:
Enumerable.Except()
плюс пользовательскиеIEqualityComparer<>
, вдоль этих линий:
-
class IdComparer: IEqualityComparer<Person> { /* boilerplate Equals(), GetHashCode() */ }
lst1.Except(lst2, new IdComparer())
.Select(p=>p.Name); // => { "Ann" }
Этот метод является громоздким для определения равенства c riteria.
- с использованием отрицания
.Contains()
- по-прежнему требуетсяIEqualityComparer<>
; или с отрицанием.Any()
- это позволяет указать условие inline.
-
from p1 in lst1
where ! lst2.Any(p2 => p1.Id == p2.Id)
select p1.Name; // => { "Ann" }
Это проще в использовании, но читается как "для каждого элемента в lst1 проверить каждый элемент в lst2", который выглядит как сложность O (M * N). Не уверен, что различные поставщики Linq (могут) оптимизируют это.
Сложность, тарифы .Except()
довольно немного лучше: примерно O (M + N), as it uses a Set<>
.
- Как насчет трюка «left-outer-join-filter-by-NULLs» из Sql? Я не нашел ссылок на это, так что либо я не искал достаточно, либо это было испорчено.
-
from p1 in lst1
join p2 in lst2 on p1.Id equals p2.Id into grp
where ! grp.Any()
select p1.Name; // => { "Ann" }
Это позволяет легко сравнивать с помощью поля.
Кроме того, из того, что я могу сказать (вникая в реализацию Enumerable.JoinIterator()
), сложность по-прежнему примерно равна O (M + N).
Является ли это хорошей заменой для Enumerable.Except()
?
Вы можете написать простое проектирование '' EqualityComparer так что вам нужно только написать шаблонные один раз. –
CodesInChaos
Как насчет переопределения методов 'Equals' и' GetHashCode' вашего класса? –
@CodesInChaos True - но метод «внешнего соединения» даже позволяет работать с коллекциями разных типов, если существует общий «ключ», а шаблон - 0 –