diff --git a/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs index 834fcdad006d7d..73983cf87fbde8 100644 --- a/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/OfType.SpeedOpt.cs @@ -52,17 +52,21 @@ public override TResult[] ToArray() public override List ToList() { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); foreach (object? item in _source) { if (item is TResult castItem) { - list.Add(castItem); + builder.Add(castItem); } } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override TResult? TryGetFirst(out bool found) diff --git a/src/libraries/System.Linq/src/System/Linq/SegmentedArrayBuilder.cs b/src/libraries/System.Linq/src/System/Linq/SegmentedArrayBuilder.cs index 0abaab4156bb8a..e6b72d08885369 100644 --- a/src/libraries/System.Linq/src/System/Linq/SegmentedArrayBuilder.cs +++ b/src/libraries/System.Linq/src/System/Linq/SegmentedArrayBuilder.cs @@ -5,6 +5,7 @@ using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace System.Collections.Generic { @@ -251,6 +252,27 @@ public readonly T[] ToArray() return result; } + /// Creates a list containing all of the elements in the builder. + public readonly List ToList() + { + List result; + int count = Count; + + if (count != 0) + { + result = new List(count); + + CollectionsMarshal.SetCount(result, count); + ToSpanInlined(CollectionsMarshal.AsSpan(result)); + } + else + { + result = []; + } + + return result; + } + /// Creates an array containing all of the elements in the builder. /// The number of extra elements of room to allocate in the resulting array. public readonly T[] ToArray(int additionalLength) diff --git a/src/libraries/System.Linq/src/System/Linq/Select.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Select.SpeedOpt.cs index f491f1f0de015a..98c686e514b68c 100644 --- a/src/libraries/System.Linq/src/System/Linq/Select.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Select.SpeedOpt.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static System.Linq.Utilities; @@ -31,15 +32,19 @@ public override TResult[] ToArray() public override List ToList() { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); Func selector = _selector; foreach (TSource item in _source) { - list.Add(selector(item)); + builder.Add(selector(item)); } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override int GetCount(bool onlyIfCheap) @@ -657,7 +662,7 @@ public override IEnumerable Select(Func s return sourceFound ? _selector(input!) : default!; } - private TResult[] LazyToArray() + private TResult[] ToArrayNoPresizing() { Debug.Assert(_source.GetCount(onlyIfCheap: true) == -1); @@ -691,12 +696,31 @@ public override TResult[] ToArray() int count = _source.GetCount(onlyIfCheap: true); return count switch { - -1 => LazyToArray(), + -1 => ToArrayNoPresizing(), 0 => [], _ => PreallocatingToArray(count), }; } + private List ToListNoPresizing() + { + Debug.Assert(_source.GetCount(onlyIfCheap: true) == -1); + + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); + + Func selector = _selector; + foreach (TSource input in _source) + { + builder.Add(selector(input)); + } + + List result = builder.ToList(); + builder.Dispose(); + + return result; + } + public override List ToList() { int count = _source.GetCount(onlyIfCheap: true); @@ -704,11 +728,7 @@ public override List ToList() switch (count) { case -1: - list = new List(); - foreach (TSource input in _source) - { - list.Add(_selector(input)); - } + list = ToListNoPresizing(); break; case 0: list = new List(); diff --git a/src/libraries/System.Linq/src/System/Linq/SelectMany.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/SelectMany.SpeedOpt.cs index ae0bf35ef8f1af..a521ab37295dbd 100644 --- a/src/libraries/System.Linq/src/System/Linq/SelectMany.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/SelectMany.SpeedOpt.cs @@ -48,15 +48,19 @@ public override TResult[] ToArray() public override List ToList() { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); Func> selector = _selector; - foreach (TSource element in _source) + foreach (TSource item in _source) { - list.AddRange(selector(element)); + builder.AddRange(selector(item)); } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } } } diff --git a/src/libraries/System.Linq/src/System/Linq/SkipTake.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/SkipTake.SpeedOpt.cs index 1a49488b09fbb7..5cb53aa2997e06 100644 --- a/src/libraries/System.Linq/src/System/Linq/SkipTake.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/SkipTake.SpeedOpt.cs @@ -491,8 +491,6 @@ public override TSource[] ToArray() public override List ToList() { - var list = new List(); - using (IEnumerator en = _source.GetEnumerator()) { if (SkipBeforeFirst(en) && en.MoveNext()) @@ -500,16 +498,23 @@ public override List ToList() int remaining = Limit - 1; // Max number of items left, not counting the current element. int comparand = HasLimit ? 0 : int.MinValue; // If we don't have an upper bound, have the comparison always return true. + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); do { remaining--; - list.Add(en.Current); + builder.Add(en.Current); } while (remaining >= comparand && en.MoveNext()); + + List result = builder.ToList(); + builder.Dispose(); + + return result; } } - return list; + return []; } private bool SkipBeforeFirst(IEnumerator en) => SkipBefore(_minIndexInclusive, en); diff --git a/src/libraries/System.Linq/src/System/Linq/Where.SpeedOpt.cs b/src/libraries/System.Linq/src/System/Linq/Where.SpeedOpt.cs index 40a05db9abf8ff..ecc7be996b1e45 100644 --- a/src/libraries/System.Linq/src/System/Linq/Where.SpeedOpt.cs +++ b/src/libraries/System.Linq/src/System/Linq/Where.SpeedOpt.cs @@ -55,18 +55,22 @@ public override TSource[] ToArray() public override List ToList() { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); Func predicate = _predicate; foreach (TSource item in _source) { if (predicate(item)) { - list.Add(item); + builder.Add(item); } } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override TSource? TryGetFirst(out bool found) @@ -199,17 +203,21 @@ public static TSource[] ToArray(ReadOnlySpan source, Func ToList(ReadOnlySpan source, Func predicate) { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); foreach (TSource item in source) { if (predicate(item)) { - list.Add(item); + builder.Add(item); } } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override TSource? TryGetFirst(out bool found) @@ -398,17 +406,21 @@ public static TResult[] ToArray(ReadOnlySpan source, Func ToList(ReadOnlySpan source, Func predicate, Func selector) { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); foreach (TSource item in source) { if (predicate(item)) { - list.Add(selector(item)); + builder.Add(selector(item)); } } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override TResult? TryGetFirst(out bool found) => TryGetFirst(_source, _predicate, _selector, out found); @@ -538,7 +550,8 @@ public override TResult[] ToArray() public override List ToList() { - var list = new List(); + SegmentedArrayBuilder.ScratchBuffer scratch = default; + SegmentedArrayBuilder builder = new(scratch); Func predicate = _predicate; Func selector = _selector; @@ -546,11 +559,14 @@ public override List ToList() { if (predicate(item)) { - list.Add(selector(item)); + builder.Add(selector(item)); } } - return list; + List result = builder.ToList(); + builder.Dispose(); + + return result; } public override TResult? TryGetFirst(out bool found)