Я хотел бы сравнить две коллекции (в C #), но я не уверен, что это лучший способ реализовать это эффективно.
Я читал другую ветку о Enumerable.SequenceEqual , но это не совсем то, что я ищу.
В моем случае две коллекции были бы равны, если бы они содержали одни и те же элементы (независимо от порядка).
Пример:
collection1 = {1, 2, 3, 4};
collection2 = {2, 4, 1, 3};
collection1 == collection2; // true
Обычно я перебираю каждый элемент одной коллекции и проверяю, существует ли он в другой коллекции, затем перебираю каждый элемент другой коллекции и проверяю, существует ли он в первой коллекции. (Я начинаю со сравнения длин)
if (collection1.Count != collection2.Count)
return false; // the collections are not equal
foreach (Item item in collection1)
{
if (!collection2.Contains(item))
return false; // the collections are not equal
}
foreach (Item item in collection2)
{
if (!collection1.Contains(item))
return false; // the collections are not equal
}
return true; // the collections are equal
Однако это не совсем правильно, и, вероятно, это не самый эффективный способ сравнить две коллекции на равенство.
Пример, который я могу придумать, был бы неправильным:
collection1 = {1, 2, 3, 3, 4}
collection2 = {1, 2, 2, 3, 4}
Который был бы равен моей реализации. Должен ли я просто подсчитать, сколько раз найден каждый предмет, и убедиться, что количество совпадений в обеих коллекциях одинаково?
Примеры приведены в некотором роде C # (назовем это псевдо-C #), но давать ответ на любом языке, который вы пожелаете, это не имеет значения.
Примечание: я использовал целые числа в примерах для простоты, но я хочу иметь возможность также использовать объекты ссылочного типа (они не работают корректно в качестве ключей, потому что сравнивается только ссылка на объект, а не содержимое).
источник
Ответы:
Оказывается, Microsoft уже рассмотрела это в своей структуре тестирования: CollectionAssert.AreEquivalent
Используя рефлектор, я изменил код, стоящий за AreEquivalent (), чтобы создать соответствующий компаратор равенства. Он является более полным, чем существующие ответы, так как он принимает во внимание пустые значения, реализует IEqualityComparer и имеет некоторые проверки эффективности и крайних случаев. плюс это Microsoft :)
Пример использования:
Или, если вы просто хотите сравнить две коллекции напрямую:
Наконец, вы можете использовать свой компаратор равенства по вашему выбору:
источник
EqualityComparer
(либо предоставленным вами, либоEqualityComparer.Default
вы можете проверить Reflector или источник ссылки, чтобы проверить это). True, если объекты изменяются (и, в частности, изменяются их хэш-коды) во время работы этого метода, тогда результаты являются неожиданными, но это просто означает, что этот метод не является поточно-ориентированным в этом контексте.EqualityComparer
(или,EqualityComparer.Default
если ни один не был указан), и снова реализация верна.Equals
из-заIEqualityComparer<T>
интерфейса. То, на что вы должны смотреть - это имя самого компаратора . В этом случае этоMultiSetComparer
имеет смысл.Простое и довольно эффективное решение - отсортировать обе коллекции и сравнить их на равенство:
Этот алгоритм O (N * logN), а ваше решение выше O (N ^ 2).
Если коллекции имеют определенные свойства, возможно, вы сможете реализовать более быстрое решение. Например, если обе ваши коллекции являются хэш-наборами, они не могут содержать дубликаты. Кроме того, проверка того, содержит ли хеш-набор какой-либо элемент, выполняется очень быстро. В этом случае алгоритм, похожий на ваш, вероятно, будет самым быстрым.
источник
Создайте словарь «dict», а затем для каждого члена первой коллекции выполните dict [member] ++;
Затем таким же образом переберите второй набор, но для каждого члена выполните dict [member] -.
В конце переберите все элементы в словаре:
Изменить: Насколько я могу сказать, это в том же порядке, что и наиболее эффективный алгоритм. Этот алгоритм O (N), предполагая, что Словарь использует O (1) поисков.
источник
return dict.All(kvp => kvp.Value == 0);
Это моя (под сильным влиянием Д. Дженнингса) общая реализация метода сравнения (в C #):
источник
The keys of a dictionary are compared by reference, so we have to find the original key that is equivalent to the "item"
- это неправда. Алгоритм основан на неправильных предположениях, и хотя он работает, он ужасно неэффективен.Вы могли бы использовать Hashset . Посмотрите на метод SetEquals .
источник
Если вы используете Следует , вы можете использовать Следует с помощью Содержит.
И, наконец, вы можете написать расширение.
ОБНОВИТЬ
В методе ShouldBe существует необязательный параметр .
источник
bool ignoreOrder
на ShouldBe методе.РЕДАКТИРОВАТЬ: я понял, как только я поставил, что это действительно работает только для наборов - он не будет правильно работать с коллекциями, которые имеют дубликаты предметов. Например, {1, 1, 2} и {2, 2, 1} будут считаться равными с точки зрения этого алгоритма. Однако если ваши коллекции являются наборами (или их равенство можно измерить таким образом), я надеюсь, что вы найдете следующее полезным.
Решение, которое я использую:
Linq делает словарь под прикрытием, так что это тоже O (N). (Обратите внимание, это O (1), если коллекции не одного размера).
Я сделал проверку работоспособности, используя метод «SetEqual», предложенный Даниэлем, метод OrderBy / SequenceEquals, предложенный Игорем, и мое предложение. Результаты приведены ниже, показывая O (N * LogN) для Игоря и O (N) для моего и Дэниела.
Я думаю, что простота кода пересечения Linq делает его предпочтительным решением.
источник
В случае отсутствия повторов и порядка, следующий EqualityComparer может использоваться для разрешения коллекций в качестве ключей словаря:
Вот реализация ToHashSet (), которую я использовал. Алгоритм хэш - код приходит от Effective Java (путем Jon тарелочкам).
источник
ISet<T>
выразить, что он предназначен для наборов (то есть без дубликатов).ISet
, идея заключалась в том, чтобы рассматриватьIEnumerable
набор как (потому что у вас естьIEnumerable
для начала), несмотря на то, что 0 повышений в более чем 5 лет, которые, возможно, не были самой острой идеей: PДля решения требуется .NET 3.5 и
System.Collections.Generic
пространство имен. Согласно Microsoft ,SymmetricExceptWith
это операция O (n + m) , где n представляет количество элементов в первом наборе, а m представляет количество элементов во втором. При необходимости вы всегда можете добавить в эту функцию средство сравнения на равенство.источник
Почему бы не использовать .Except ()
http://msdn.microsoft.com/en-us/library/bb397894.aspx
источник
Except
не будет работать для подсчета дубликатов. Он вернет true для наборов {1,2,2} и {1,1,2}.[1, 1, 2] != [1, 2, 2]
. ИспользованиеDistinct
сделает их похожими.Дублирующий пост, но посмотрите мое решение для сравнения коллекций . Это довольно просто:
Это выполнит сравнение на равенство независимо от порядка:
Это проверит, были ли элементы добавлены / удалены:
Это увидит, какие элементы в словаре изменились:
Оригинальный пост здесь .
источник
Эриксон почти прав: так как вы хотите совпадать по количеству дубликатов, вам нужна сумка . В Java это выглядит примерно так:
Я уверен, что C # имеет встроенную реализацию Set. Я бы использовал это первым; если производительность является проблемой, вы всегда можете использовать другую реализацию Set, но использовать тот же интерфейс Set.
источник
Вот мой вариант метода ответа ohadsc на случай, если он кому-нибудь пригодится
источник
IEnumerable<T>
s являются запросами, то вызовCount()
не является хорошей идеей. Подход оригинального ответа Охада - проверить, являются ли ониICollection<T>
лучшей идеей.Вот решение, которое является улучшением по сравнению с этим .
источник
Есть много решений этой проблемы. Если вам не нужны дубликаты, вам не нужно сортировать оба. Сначала убедитесь, что у них одинаковое количество предметов. После этого сортируйте одну из коллекций. Затем найдите каждый элемент из второй коллекции в отсортированной коллекции. Если вы не нашли данный элемент, остановитесь и верните false. Сложность этого: - сортировка первой коллекции: N Log (N) - поиск каждого элемента от второго до первого: NЗафиксируйте (N), чтобы вы получили 2 * N * LOG (N), предполагая, что они совпадают, и вы ищете все. Это похоже на сложность сортировки обоих. Кроме того, это дает вам возможность остановиться раньше, если есть разница. Однако имейте в виду, что если оба отсортированы, прежде чем вы приступите к этому сравнению, и вы попытаетесь отсортировать, используя что-то вроде qsort, сортировка будет более дорогой. Для этого есть оптимизации. Другая альтернатива, которая отлично подходит для небольших коллекций, в которых вы знаете диапазон элементов, - это использование индекса битовой маски. Это даст вам производительность O (n). Другая альтернатива - использовать хеш и искать его. Для небольших коллекций обычно намного лучше выполнить сортировку или индекс битовой маски. У Hashtable есть недостаток худшего местоположения, так что имейте это в виду. Опять же, это только если вы не заботиться о дубликатах. Если вы хотите учесть дубликаты, перейдите к сортировке обоих.
источник
Во многих случаях единственным подходящим ответом является ответ Игоря Островского, другие ответы основаны на хэш-коде объектов. Но когда вы генерируете хеш-код для объекта, вы делаете это только на основе его полей IMMUTABLE, таких как поле идентификатора объекта (в случае объекта базы данных). Почему важно переопределить GetHashCode, когда метод Equals переопределен?
Это означает, что при сравнении двух коллекций результат может быть верным для метода сравнения, даже если поля разных элементов не равны. Для глубокого сравнения коллекций необходимо использовать метод Игоря и реализовать IEqualirity.
Пожалуйста, прочитайте комментарии меня и mr.Schnider's к его самому популярному сообщению.
Джеймс
источник
С учетом дубликатов в
IEnumerable<T>
(если наборы нежелательны \ возможны) и «игнорируя порядок», вы должны иметь возможность использовать.GroupBy()
.Я не эксперт по измерениям сложности, но мое элементарное понимание состоит в том, что это должно быть O (n). Я понимаю, что O (n ^ 2) приходит от выполнения операции O (n) внутри другой операции O (n), например
ListA.Where(a => ListB.Contains(a)).ToList()
. Каждый элемент в ListB оценивается на равенство с каждым элементом в ListA.Как я уже сказал, мое понимание сложности ограничено, поэтому поправьте меня, если я ошибаюсь.
источник
Это простое решение заставляет
IEnumerable
реализовать универсальный типIComparable
. Из-заOrderBy
определения России.Если вы не хотите делать такое предположение, но по-прежнему хотите использовать это решение, вы можете использовать следующий фрагмент кода:
источник
При сравнении для целей утверждений модульного тестирования может иметь смысл выбросить некоторую эффективность в окно и просто преобразовать каждый список в строковое представление (csv) перед выполнением сравнения. Таким образом, стандартное тестовое сообщение подтверждения будет отображать различия в сообщении об ошибке.
Использование:
Метод расширения помощника:
источник