Я пытаюсь определить, являются ли несколько значений объектов в коллекции одинаковыми или нет. Я придумал два основных способа сделать это; но ни один из них не является полностью удовлетворительным. В обоих случаях я делаю проверку, сравнивая значения каждого объекта с теми, что указаны в первой коллекции. Во-первых, повторить итерацию коллекции, проверяя каждое значение в одно и то же время:
bool string1Varies = false;
bool string2Varies = false;
bool string3Varies = false;
bool string4Varies = false;
foreach (Foo foo in myFooList)
{
if (foo.string1 != myFooList[0].string1)
string1Varies = true;
if (foo.string2 != myFooList[0].string2)
string2Varies = true;
if (foo.string3 != myFooList[0].string3)
string3Varies = true;
if (foo.string4 != myFooList[0].string4)
string4Varies = true;
//optionally break if all 4 bools are true
}
Если нормальный случай состоит в том, что все значения одинаковы, этот подход имеет то преимущество, что только один раз повторяется сбор, но он несколько подробный. Я мог бы улучшить свою производительность, если бы все 4 варианта были несколько обычными, добавив проверку на разрыв цикла; но это добавит больше накладных расходов на итерацию и сделает ее еще более продолжительной.
В качестве альтернативы я мог вычислять каждый bool индивидуально с помощью Linq:
bool string1Varies = myFooList.All(foo => foo.string1 = myFooList[0].string1);
bool string2Varies = myFooList.All(foo => foo.string2 = myFooList[0].string2);
bool string3Varies = myFooList.All(foo => foo.string3 = myFooList[0].string3);
bool string4Varies = myFooList.All(foo => foo.string4 = myFooList[0].string4);
Преимущество этого заключается в том, что оно кратковременно и быстро заканчивается, если изменение происходит на ранних этапах сбора, но если значения совпадают, это приведет к повторному повторению всей коллекции 4 раза.
Есть ли способ получить лучшее из обоих миров, вычислив все 4 значения в одной легко читаемой операции Linq?
Просто обратите внимание, что нет (практических) оптимизаций, чтобы сделать это быстрее. Для выполнения этой проверки вам понадобятся итерации цикла Θ (n) (с несколькими предположениями).
Ваши условия не имеют зависимости друг от друга, поэтому они должны оставаться независимыми. Изменение многословия, которое вы могли бы сделать, включает переписывание ваших условий в вашем первом решении.
string1Varies |= foo.string1 != myFooList[0].string1;
string2Varies |= foo.string2 != myFooList[0].string2;
string3Varies |= foo.string3 != myFooList[0].string3;
string4Varies |= foo.string4 != myFooList[0].string4;
Я не вижу ничего подобного.
Как правильно указал Обувь, если ваши сравнения и несоответствия полностью независимы, насколько я знаю, нет никакого способа сделать это более результативным и сжатым способом, чем утверждение foreach (ваше первое решение). Однако решение Linq можно улучшить, используя Any
вместо All
:
bool string1Varies = myFooList.Any(foo => foo.string1 != myFooList[0].string1);
bool string2Varies = myFooList.Any(foo => foo.string2 != myFooList[0].string2);
bool string3Varies = myFooList.Any(foo => foo.string3 != myFooList[0].string3);
bool string4Varies = myFooList.Any(foo => foo.string4 != myFooList[0].string4);
Таким образом, если одно из значений отличается, вы не завершаете повторение всей коллекции. Но обратите внимание, что он все еще хуже, чем решение foreach, из-за нескольких итераций.
Для сравнения любой разницы в свойствах объекта:
IEnumerable<long> hashed = myList.Select(
(item) => new string[] { item.String1, item.String2, item.String3, item.String4 }
.Aggregate<string, long>(0, (runningHash, actual) =>
(runningHash + actual.GetHashCode()) * 2654435761 % (int)Math.Pow(2, 32)));
bool areAllSame = hashed.Skip(1)
.FirstOrDefault((item) => item != hashed.First()) == null;
Что он делает: он создает бегущую хеш-функцию (с алгоритмом хэш-алгоритма) по всем свойствам. GetHashCode не должен быть криптографическим, чтобы сделать конфликты невероятными. Столкновения в этой области очень маловероятны, но, конечно, не невозможны. В противном случае вы всегда можете использовать криптографическую хеш-функцию для безопасной работы. Порядок строк также покрывается, так как текущее значение возвращается для каждого значения.
Не жалуйтесь на детали, воспринимайте это как другой подход. → Один хеш по всем свойствам, которые делают равенство прямо сопоставимым в одном цикле.
Это O (n * m) в худшем случае... (m = количество свойств)