When to use LINQ's .ToList() or .ToArray()
Asked Answered
E

2

17

After running this code:

var input = new List<T>( ... );
var result = input.Select( t => new U(t) );

U first1 = null;
foreach ( U u1 in result )
    if ( first1 == null )
        first1 = u1;

U first2 = null;
foreach ( U u2 in result )
    if ( first2 == null )
        first2 = u2;

Then 'first1 == first2' evaluates to false even though both U's wrap the same T. I haven't tested it yet, but I think it can be made to evaluate to true by chaining a .ToList() or .ToArray() onto the Select() call.

In real code, which is much more complex than this simple illustration, what is a good rule of thumb to use for deciding if .ToList() or .ToArray() should be appended? My initial thoughts are either any referenced expression that may be iterated more than once or, to be safer in case potential iterations are not obvious, any referenced expression whose result will never change.

Equity answered 18/8, 2012 at 1:6 Comment(5)
Are you sure first1 == first2 is false? Iterating on the same collection should return the same objects every time.Ten
Yes, I am sure. It is the same expression but not the same collection.Equity
@PeterGluck It's false because U uses standard reference equality. Each iteration causes the sequence to be re-evaluated, so you get new U instances.Mclaurin
possible duplicate of Is it better to call ToList() or ToArray() in LINQ queries?Confectionery
Is the problem that you create a new U every time you iterate over the collection? Or that new U(t) != new U(t)? Lazyness has consequences. Whether those consequences are desirable or not depends on the use case.Bigelow
M
21

Unfortunately, I don't think there is a good "hard and fast" rule here. It depends a lot on how you expect the results to be used, and what the query itself is actually doing.

My initial thoughts are either any expression that may be iterated more than once or, to be safer in case potential iterations are not obvious, any expression whose result will never change.

In general, if you're going to use the result of a query more than once, it's always a good idea to store it via ToList() or ToArray(). This is especially true if you're LINQ query is an "expensive" one, as it prevents the expensive operation from running more than once.

Typically, if you're only going to enumerate the results, then I would leave it as IEnumerable<T>. If you plan to store the results, or use the results more than once, then storing it in a collection can be beneficial.

The other place to watch for is if you return the results in a public API. While it's often nice to return IEnumerable<T>, depending on the expected use case, you may want to consider using ToList() to prevent the operation from being executed more than once.

As for whether to use ToList() or ToArray(), it really depends on how you'll use the results. The cost associated with each is nearly identical (ToList() actually has slightly lower execution overhead if the input is not ICollection<T>). Typically, I prefer ToList() over ToArray() unless I have a specific need or desire for an array.

Mclaurin answered 18/8, 2012 at 1:10 Comment(3)
Great point about the public return value. I've run into that case before.Equity
And the same precaution applies when you're passing the result into another method.Equity
@Equity - If it's just passed to another method and it's still internal to the same code base then I don't think you should add ToList() 'protections' which will prevent future potential more than once executions. You should have enough control on your code base to refactor it if the need arise in the future. See here how public != publishCoeternity
C
3

ToList calls List<T>(IEnumerable<T>) constructor to create a List<T>, while ToArrary uses an internal class Buffer<T> to grow the array.

If the source collection (IEnumerable<T>) implements the ICollection interface, the two methods use similar code logic to copy the data.

ICollection.CopyTo(array, 0);  

Otherwise, ToList will creates the List<T> dynamically. While ToArray copies the element one by one into a new array. If the array is full, the method doubles the array size to hold the data. Finally the method returns another array based on the source collection’s size.

Cantal answered 18/8, 2012 at 1:12 Comment(2)
FYI - ToList() and ToArray() use nearly identical construction mechanisms (doubling the array size, etc). The main difference is that ToArray is potentially more expensive as it usually needs one extra array copy at the very end, as it has to get the output size to match the capacity.Mclaurin
@Equity FYI - Your question title suggests this answer - but the question text does not (I tried to include both options in my answer because of that)Mclaurin

© 2022 - 2024 — McMap. All rights reserved.