С# LINQ First() быстрее, чем ToArray() [0]?
Я запускаю тест.
Похоже:
метод 1)
List<int> = new List<int>{1,2,4, .....} //assume 1000k
var result ErrorCodes.Where(x => ReturnedErrorCodes.Contains(x)).First();
метод 2)
List<int> = new List<int>{1,2,4, .....} //assume 1000k
var result = ErrorCodes.Where(x => ReturnedErrorCodes.Contains(x)).ToArray()[0];
Почему метод 2 настолько медленный по сравнению с методом 1?
Ответы
Ответ 1
Erm... потому что вы создаете дополнительный массив (а не просто итератор). Первый подход останавливается после первого совпадения (Where
- небуферизованный потоковый API). Второй загружает все совпадения в массив (предположительно с несколькими повторными размерами), затем берет первый элемент.
В качестве побочного примечания; вы можете создавать бесконечные последовательности; первый подход все равно будет работать, второй - навсегда (или взорваться).
Он также может быть:
var result ErrorCodes.First(x => ReturnedErrorCodes.Contains(x));
(это не сделает его быстрее, но, возможно, легче читать)
Ответ 2
У вас есть кувшин, содержащий тысячу монет, многие из которых - десять центов. Вы хотите копейки. Вот два способа решения вашей проблемы:
-
Вытащите монеты из банки, по одному за раз, пока не получите копейку. Теперь у вас есть копейка.
-
Вытащите монеты из банки, по одному, положив копейки в другую банку. Если эта банка окажется слишком маленькой, переместите их по одному за раз в большую банку. Продолжайте делать это, пока у вас не будет всех копейки в финальной банке. Эта банка, вероятно, слишком большая. Произведите банку, которая достаточно велика, чтобы удержать это много копейки, а затем переместить десятину по одному за раз в новую банку. Теперь начните брать копейки из этой банки. Выньте первый. Теперь у вас есть копейка.
Теперь ясно, почему метод 1 намного быстрее, чем метод 2?
Ответ 3
Из-за отложенного выполнения.
Код ErrorCodes.Where(x => ReturnedErrorCodes.Contains(x))
не возвращает набор целых чисел, вместо этого он возвращает выражение, способное возвращать поток целых чисел. Он не выполняет никакой реальной работы, пока вы не начнете читать целые числа.
Метод ToArray
будет потреблять весь поток и помещать все целые числа в массив. Это означает, что каждый элемент во всем списке следует сравнивать с кодами ошибок.
Метод First
, с другой стороны, получит только первый элемент из потока, а затем прекратит чтение из потока. Это сделает его намного быстрее, потому что он прекратит сравнивать элементы из списка с кодами ошибок, как только найдет совпадение.
Ответ 4
Поскольку ToArray()
копирует всю последовательность в массив.
Метод 2 должен перебирать всю последовательность для построения массива, а затем возвращает первый элемент.
Метод 1 просто повторяет достаточно последовательности, чтобы найти первый соответствующий элемент.
Ответ 5
ToArray()
просматривает всю последовательность, которую он дал, и создает и выделяет из нее.
Если вы не вызываете ToArray()
, First()
позволяет Where()
возвращать только первый элемент, который соответствует и немедленно возвращается.
Ответ 6
First() - сложность O (1)
ToArray() [0] - сложность O (n) +1
Ответ 7
var @e = array.GetEnumerator();
// First
@e.MoveNext();
return @e.Current;
// ToArray (with yield [0] should as fast as First...)
while (@e.MoveNext() {
yield return @e.Current;
}
Ответ 8
Потому что во втором примере вы фактически преобразуете IEnumerable<T>
в массив, тогда как в первом примере преобразование не происходит.
Ответ 9
В методе 2 весь массив сначала необходимо преобразовать в массив. Кроме того, кажется неудобным смешивать доступ к массиву, когда First()
настолько читабельнее.
Ответ 10
Это имеет смысл, ToArray, вероятно, включает в себя копию, которая всегда будет дороже, так как Linq не может гарантировать каких-либо гарантий того, как вы собираетесь использовать свой массив, в то время как First() может просто вернуть одиночный элемент в начале.