linq .Cast<> or cast inside ConvertAll for a list

寵の児 提交于 2020-01-11 11:12:51

问题


Consider you have to convert mylist of type List<T> to List<Base> where T is subclass of Base

Are these solutions the same? Which has better performances, and why? When should I prefer using the first or the second?

return mylist.Cast<Base>().ToList();

return mylist.ConvertAll(x => (Base)x);

Maybe the second solution could be better because mylist is converted directly.

In the first solution the list is converted to IEnumerable, then to list, but I'm not sure.


回答1:


TL;DR: ConvertAll makes 1 memory allocation, but .Cast.ToList more than one in most cases.

Most LINQ extensions (like .Cast<T>()) result in a deferred execution IEnumerable<T> that can't be cast to ICollection<T> (can't get the .Count of the result).

When the result can be cast to ICollection<T>, .ToList and .ToArray can make just one memory allocation to copy the elements, but when it can't:

  • initially 4 element buffer array is allocated for non-empty source
  • when space is needed for more elements, new array is allocated with double the size of the previous one
  • elements are copied from the old array to the new one, and the old array is later deallocated by the garbage collector.

Update

Surprisingly, the difference doesn't seem nowhere near as significant as I expected:

method      elapsed             ratio               count

Cast.ToList 00:00:14.4487329    1.3719890831991     123456789
ConvertAll  00:00:10.5312302    0.728868773261865

Cast.ToList 00:00:01.4959734    1.50233158227713    12345678
ConvertAll  00:00:00.9957678    0.665632016125407

Cast.ToList 00:00:00.1252968    2.45948743599897    1234567
ConvertAll  00:00:00.0509442    0.40658878161491

Cast.ToList 00:00:00.0082611    3.99145006839945    123456
ConvertAll  00:00:00.0020697    0.250535515380002

Cast.ToList 00:00:00.0008097    0.620558719826417   12345
ConvertAll  00:00:00.0013049    1.61145104895105

Cast.ToList 00:00:00.0001812    0.193207547169811   1234
ConvertAll  00:00:00.0009378    5.17578125

Cast.ToList 00:00:00.0001433    0.149501661129568   123
ConvertAll  00:00:00.0009587    6.68888888888889

So, race your horses!

int c = 123; var L = Enumerable.Range(0, c).ToList();

GC.Collect(); var sw1 = Stopwatch.StartNew(); L.Cast<object>().ToList(); sw1.Stop();

GC.Collect(); var sw2 = Stopwatch.StartNew(); L.ConvertAll(i => (object)i); sw2.Stop();

MessageBox.Show($"Cast.ToList\t{sw1.Elapsed}\t{(double)sw1.ElapsedTicks / sw2.ElapsedTicks}\n" +
                $"ConvertAll \t{sw2.Elapsed}\t{(double)sw2.ElapsedTicks / sw1.ElapsedTicks}");


来源:https://stackoverflow.com/questions/42427912/linq-cast-or-cast-inside-convertall-for-a-list

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!