Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 872bb70

Browse files
committed
Optimize array/list.Select(...).ToArray()
LINQ already special-cases the very common case of using Select on input T[]s and List<T>s, but it doesn't take this to the next step of optimizing the case where that's then turned back into an array, a common pattern in its own right, e.g. var names = people.Select(p => p.Name).ToArray(); This commit does so, modifying the internal Iterator type to support customization of ToArray, and updating the internal Buffer type to use it. Changing Buffer not only helps ToArray but also Reverse and OrderBy, both of which convert the input sequence into an array internally.
1 parent c581dcd commit 872bb70

File tree

1 file changed

+58
-8
lines changed

1 file changed

+58
-8
lines changed

src/System.Linq/src/System/Linq/Enumerable.cs

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,11 @@ public IEnumerable<TResult> Select<TResult>(Func<TSource, TResult> selector)
144144

145145
public abstract IEnumerable<TSource> Where(Func<TSource, bool> predicate);
146146

147+
public virtual TSource[] ToArray()
148+
{
149+
return new Buffer<TSource>(this, queryInterfaces: false).ToArray();
150+
}
151+
147152
object IEnumerator.Current
148153
{
149154
get { return Current; }
@@ -431,6 +436,21 @@ public override IEnumerable<TResult> Where(Func<TResult, bool> predicate)
431436
{
432437
return new WhereEnumerableIterator<TResult>(this, predicate);
433438
}
439+
440+
public override TResult[] ToArray()
441+
{
442+
if (_predicate != null)
443+
{
444+
return base.ToArray();
445+
}
446+
447+
var results = new TResult[_source.Length];
448+
for (int i = 0; i < results.Length; i++)
449+
{
450+
results[i] = _selector(_source[i]);
451+
}
452+
return results;
453+
}
434454
}
435455

436456
internal class WhereSelectListIterator<TSource, TResult> : Iterator<TResult>
@@ -486,6 +506,21 @@ public override IEnumerable<TResult> Where(Func<TResult, bool> predicate)
486506
{
487507
return new WhereEnumerableIterator<TResult>(this, predicate);
488508
}
509+
510+
public override TResult[] ToArray()
511+
{
512+
if (_predicate != null)
513+
{
514+
return base.ToArray();
515+
}
516+
517+
var results = new TResult[_source.Count];
518+
for (int i = 0; i < results.Length; i++)
519+
{
520+
results[i] = _selector(_source[i]);
521+
}
522+
return results;
523+
}
489524
}
490525

491526
//public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) {
@@ -3079,21 +3114,35 @@ internal struct Buffer<TElement>
30793114
internal TElement[] items;
30803115
internal int count;
30813116

3082-
internal Buffer(IEnumerable<TElement> source)
3117+
internal Buffer(IEnumerable<TElement> source, bool queryInterfaces = true)
30833118
{
30843119
TElement[] items = null;
30853120
int count = 0;
3086-
ICollection<TElement> collection = source as ICollection<TElement>;
3087-
if (collection != null)
3121+
3122+
if (queryInterfaces)
30883123
{
3089-
count = collection.Count;
3090-
if (count > 0)
3124+
Enumerable.Iterator<TElement> iterator = source as Enumerable.Iterator<TElement>;
3125+
if (iterator != null)
30913126
{
3092-
items = new TElement[count];
3093-
collection.CopyTo(items, 0);
3127+
items = iterator.ToArray();
3128+
count = items.Length;
3129+
}
3130+
else
3131+
{
3132+
ICollection<TElement> collection = source as ICollection<TElement>;
3133+
if (collection != null)
3134+
{
3135+
count = collection.Count;
3136+
if (count > 0)
3137+
{
3138+
items = new TElement[count];
3139+
collection.CopyTo(items, 0);
3140+
}
3141+
}
30943142
}
30953143
}
3096-
else
3144+
3145+
if (items == null)
30973146
{
30983147
foreach (TElement item in source)
30993148
{
@@ -3109,6 +3158,7 @@ internal Buffer(IEnumerable<TElement> source)
31093158
count++;
31103159
}
31113160
}
3161+
31123162
this.items = items;
31133163
this.count = count;
31143164
}

0 commit comments

Comments
 (0)