Enumerable.ToList(source) is essentially just a call to new List(source) .
This constructor will check if the source is ICollection<T> , and if it allocates an array of the appropriate size. In other cases, that is, in most cases when the source is a LINQ query, it will allocate an array with the initial initial capacity by default (four elements) and increase it, doubling the capacity as necessary. Each time the capacity doubles, a new array is allocated, and the old one to a new one.
This can lead to some overhead in cases where your list has a lot of items (we probably say at least thousands of thousands). The overhead can be significant as soon as the list grows by more than 85 KB, as it is then allocated to a bunch of large objects that are not compacted and can suffer from memory fragmentation. Please note that I am referencing the array in the list. If T is a reference type, this array contains only references, not the actual objects. These objects then do not take into account the 85 KB limit.
You can remove some of this overhead if you can accurately estimate the size of your sequence (where itβs better to overestimate a bit than underestimate a bit). For example, if you use only the .Select() operator for what implements ICollection<T> , you know the size of the output list.
In such cases, this extension method will reduce this overhead:
public static List<T> ToList<T>(this IEnumerable<T> source, int initialCapacity) { // parameter validation ommited for brevity var result = new List<T>(initialCapacity); foreach (T item in source) { result.Add(item); } return result; }
In some cases, the list you created will simply replace the list that already exists, for example. from the previous launch. In these cases, you can avoid quite a bit of memory allocation if you reuse the old list. This will only work if you do not have simultaneous access to this old list, and I would not do this if the new lists will usually be much smaller than the old lists. In this case, you can use this extension method:
public static void CopyToList<T>(this IEnumerable<T> source, List<T> destination) {
.ToList() that, do I .ToList() inefficient? Not if you have memory and you are going to use the list repeatedly, either for random indexing into it a lot, or repeating it several times.
Now back to your specific example:
var matches = (from x in list1 join y in list2 on x equals y select x).ToList();
It may be more efficient for this in another way, for example:
var matches = list1.Intersect(list2).ToList();
which will give the same results if list1 and list2 do not contain duplicates and are very effective if list2 is small.
The only way to find out about this, as usual, is to measure using typical workloads.