Improve Dictionary TryGetValue size/perfomance (dotnet/coreclr#27195)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / Array.cs
blob06f8bba0185249e648adf2ea6e5b058a5673f9ed
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 using System.Collections;
6 using System.Collections.Generic;
7 using System.Collections.ObjectModel;
8 using System.Diagnostics;
9 using System.Diagnostics.CodeAnalysis;
10 using System.Runtime.CompilerServices;
11 using Internal.Runtime.CompilerServices;
13 namespace System
15 [Serializable]
16 [System.Runtime.CompilerServices.TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")]
17 public abstract partial class Array : ICloneable, IList, IStructuralComparable, IStructuralEquatable
19 // We impose limits on maximum array length in each dimension to allow efficient
20 // implementation of advanced range check elimination in future.
21 // Keep in sync with vm\gcscan.cpp and HashHelpers.MaxPrimeArrayLength.
22 // The constants are defined in this method: inline SIZE_T MaxArrayLength(SIZE_T componentSize) from gcscan
23 // We have different max sizes for arrays with elements of size 1 for backwards compatibility
24 internal const int MaxArrayLength = 0X7FEFFFFF;
25 internal const int MaxByteArrayLength = 0x7FFFFFC7;
27 // This ctor exists solely to prevent C# from generating a protected .ctor that violates the surface area.
28 private protected Array() { }
30 public static ReadOnlyCollection<T> AsReadOnly<T>(T[] array)
32 if (array == null)
34 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
37 // T[] implements IList<T>.
38 return new ReadOnlyCollection<T>(array);
41 public static void Resize<T>([NotNull] ref T[]? array, int newSize)
43 if (newSize < 0)
44 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.newSize, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
46 T[]? larray = array;
47 if (larray == null)
49 array = new T[newSize];
50 return;
53 if (larray.Length != newSize)
55 T[] newArray = new T[newSize];
56 Copy(larray, 0, newArray, 0, larray.Length > newSize ? newSize : larray.Length);
57 array = newArray;
61 public static Array CreateInstance(Type elementType, params long[] lengths)
63 if (lengths == null)
65 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.lengths);
67 if (lengths.Length == 0)
68 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NeedAtLeast1Rank);
70 int[] intLengths = new int[lengths.Length];
72 for (int i = 0; i < lengths.Length; ++i)
74 long len = lengths[i];
75 int ilen = (int)len;
76 if (len != ilen)
77 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.len, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
78 intLengths[i] = ilen;
81 return Array.CreateInstance(elementType, intLengths);
84 public static void Copy(Array sourceArray, Array destinationArray, long length)
86 int ilength = (int)length;
87 if (length != ilength)
88 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
90 Copy(sourceArray, destinationArray, ilength);
93 public static void Copy(Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length)
95 int isourceIndex = (int)sourceIndex;
96 int idestinationIndex = (int)destinationIndex;
97 int ilength = (int)length;
99 if (sourceIndex != isourceIndex)
100 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceIndex, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
101 if (destinationIndex != idestinationIndex)
102 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.destinationIndex, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
103 if (length != ilength)
104 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
106 Copy(sourceArray, isourceIndex, destinationArray, idestinationIndex, ilength);
109 public object? GetValue(long index)
111 int iindex = (int)index;
112 if (index != iindex)
113 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
115 return this.GetValue(iindex);
118 public object? GetValue(long index1, long index2)
120 int iindex1 = (int)index1;
121 int iindex2 = (int)index2;
123 if (index1 != iindex1)
124 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index1, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
125 if (index2 != iindex2)
126 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index2, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
128 return this.GetValue(iindex1, iindex2);
131 public object? GetValue(long index1, long index2, long index3)
133 int iindex1 = (int)index1;
134 int iindex2 = (int)index2;
135 int iindex3 = (int)index3;
137 if (index1 != iindex1)
138 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index1, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
139 if (index2 != iindex2)
140 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index2, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
141 if (index3 != iindex3)
142 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index3, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
144 return this.GetValue(iindex1, iindex2, iindex3);
147 public object? GetValue(params long[] indices)
149 if (indices == null)
150 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices);
151 if (Rank != indices.Length)
152 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices);
154 int[] intIndices = new int[indices.Length];
156 for (int i = 0; i < indices.Length; ++i)
158 long index = indices[i];
159 int iindex = (int)index;
160 if (index != iindex)
161 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
162 intIndices[i] = iindex;
165 return this.GetValue(intIndices);
168 public void SetValue(object? value, long index)
170 int iindex = (int)index;
172 if (index != iindex)
173 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
175 this.SetValue(value, iindex);
178 public void SetValue(object? value, long index1, long index2)
180 int iindex1 = (int)index1;
181 int iindex2 = (int)index2;
183 if (index1 != iindex1)
184 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index1, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
185 if (index2 != iindex2)
186 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index2, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
188 this.SetValue(value, iindex1, iindex2);
191 public void SetValue(object? value, long index1, long index2, long index3)
193 int iindex1 = (int)index1;
194 int iindex2 = (int)index2;
195 int iindex3 = (int)index3;
197 if (index1 != iindex1)
198 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index1, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
199 if (index2 != iindex2)
200 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index2, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
201 if (index3 != iindex3)
202 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index3, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
204 this.SetValue(value, iindex1, iindex2, iindex3);
207 public void SetValue(object? value, params long[] indices)
209 if (indices == null)
210 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.indices);
211 if (Rank != indices.Length)
212 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankIndices);
214 int[] intIndices = new int[indices.Length];
216 for (int i = 0; i < indices.Length; ++i)
218 long index = indices[i];
219 int iindex = (int)index;
220 if (index != iindex)
221 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
222 intIndices[i] = iindex;
225 this.SetValue(value, intIndices);
228 private static int GetMedian(int low, int hi)
230 // Note both may be negative, if we are dealing with arrays w/ negative lower bounds.
231 Debug.Assert(low <= hi);
232 Debug.Assert(hi - low >= 0, "Length overflow!");
233 return low + ((hi - low) >> 1);
236 public long GetLongLength(int dimension)
238 // This method should throw an IndexOufOfRangeException for compat if dimension < 0 or >= Rank
239 return GetLength(dimension);
242 // Number of elements in the Array.
243 int ICollection.Count => Length;
245 // Returns an object appropriate for synchronizing access to this
246 // Array.
247 public object SyncRoot => this;
249 // Is this Array read-only?
250 public bool IsReadOnly => false;
252 public bool IsFixedSize => true;
254 // Is this Array synchronized (i.e., thread-safe)? If you want a synchronized
255 // collection, you can use SyncRoot as an object to synchronize your
256 // collection with. You could also call GetSynchronized()
257 // to get a synchronized wrapper around the Array.
258 public bool IsSynchronized => false;
260 object? IList.this[int index]
262 get => GetValue(index);
263 set => SetValue(value, index);
266 int IList.Add(object? value)
268 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_FixedSizeCollection);
269 return default;
272 bool IList.Contains(object? value)
274 return Array.IndexOf(this, value) >= this.GetLowerBound(0);
277 void IList.Clear()
279 Array.Clear(this, this.GetLowerBound(0), this.Length);
282 int IList.IndexOf(object? value)
284 return Array.IndexOf(this, value);
287 void IList.Insert(int index, object? value)
289 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_FixedSizeCollection);
292 void IList.Remove(object? value)
294 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_FixedSizeCollection);
297 void IList.RemoveAt(int index)
299 ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_FixedSizeCollection);
302 // Make a new array which is a shallow copy of the original array.
304 public object Clone()
306 return MemberwiseClone();
309 int IStructuralComparable.CompareTo(object? other, IComparer comparer)
311 if (other == null)
313 return 1;
316 Array? o = other as Array;
318 if (o == null || this.Length != o.Length)
320 ThrowHelper.ThrowArgumentException(ExceptionResource.ArgumentException_OtherNotArrayOfCorrectLength, ExceptionArgument.other);
323 int i = 0;
324 int c = 0;
326 while (i < o.Length && c == 0)
328 object? left = GetValue(i);
329 object? right = o.GetValue(i);
331 c = comparer.Compare(left, right);
332 i++;
335 return c;
338 bool IStructuralEquatable.Equals(object? other, IEqualityComparer comparer)
340 if (other == null)
342 return false;
345 if (object.ReferenceEquals(this, other))
347 return true;
350 if (!(other is Array o) || o.Length != this.Length)
352 return false;
355 int i = 0;
356 while (i < o.Length)
358 object? left = GetValue(i);
359 object? right = o.GetValue(i);
361 if (!comparer.Equals(left, right))
363 return false;
365 i++;
368 return true;
371 int IStructuralEquatable.GetHashCode(IEqualityComparer comparer)
373 if (comparer == null)
374 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer);
376 int ret = 0;
378 for (int i = (this.Length >= 8 ? this.Length - 8 : 0); i < this.Length; i++)
380 ret = HashCode.Combine(ret, comparer.GetHashCode(GetValue(i)!));
383 return ret;
386 // Searches an array for a given element using a binary search algorithm.
387 // Elements of the array are compared to the search value using the
388 // IComparable interface, which must be implemented by all elements
389 // of the array and the given search value. This method assumes that the
390 // array is already sorted according to the IComparable interface;
391 // if this is not the case, the result will be incorrect.
393 // The method returns the index of the given value in the array. If the
394 // array does not contain the given value, the method returns a negative
395 // integer. The bitwise complement operator (~) can be applied to a
396 // negative result to produce the index of the first element (if any) that
397 // is larger than the given search value.
399 public static int BinarySearch(Array array, object? value)
401 if (array == null)
402 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
403 return BinarySearch(array, array.GetLowerBound(0), array.Length, value, null);
406 // Searches a section of an array for a given element using a binary search
407 // algorithm. Elements of the array are compared to the search value using
408 // the IComparable interface, which must be implemented by all
409 // elements of the array and the given search value. This method assumes
410 // that the array is already sorted according to the IComparable
411 // interface; if this is not the case, the result will be incorrect.
413 // The method returns the index of the given value in the array. If the
414 // array does not contain the given value, the method returns a negative
415 // integer. The bitwise complement operator (~) can be applied to a
416 // negative result to produce the index of the first element (if any) that
417 // is larger than the given search value.
419 public static int BinarySearch(Array array, int index, int length, object? value)
421 return BinarySearch(array, index, length, value, null);
424 // Searches an array for a given element using a binary search algorithm.
425 // Elements of the array are compared to the search value using the given
426 // IComparer interface. If comparer is null, elements of the
427 // array are compared to the search value using the IComparable
428 // interface, which in that case must be implemented by all elements of the
429 // array and the given search value. This method assumes that the array is
430 // already sorted; if this is not the case, the result will be incorrect.
432 // The method returns the index of the given value in the array. If the
433 // array does not contain the given value, the method returns a negative
434 // integer. The bitwise complement operator (~) can be applied to a
435 // negative result to produce the index of the first element (if any) that
436 // is larger than the given search value.
438 public static int BinarySearch(Array array, object? value, IComparer? comparer)
440 if (array == null)
441 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
442 return BinarySearch(array, array.GetLowerBound(0), array.Length, value, comparer);
445 // Searches a section of an array for a given element using a binary search
446 // algorithm. Elements of the array are compared to the search value using
447 // the given IComparer interface. If comparer is null,
448 // elements of the array are compared to the search value using the
449 // IComparable interface, which in that case must be implemented by
450 // all elements of the array and the given search value. This method
451 // assumes that the array is already sorted; if this is not the case, the
452 // result will be incorrect.
454 // The method returns the index of the given value in the array. If the
455 // array does not contain the given value, the method returns a negative
456 // integer. The bitwise complement operator (~) can be applied to a
457 // negative result to produce the index of the first element (if any) that
458 // is larger than the given search value.
460 public static int BinarySearch(Array array, int index, int length, object? value, IComparer? comparer)
462 if (array == null)
463 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
464 int lb = array.GetLowerBound(0);
465 if (index < lb)
466 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
467 if (length < 0)
468 ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
469 if (array.Length - (index - lb) < length)
470 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
471 if (array.Rank != 1)
472 ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
474 comparer ??= Comparer.Default;
475 #if !CORERT
476 if (comparer == Comparer.Default)
478 int retval;
479 bool r = TrySZBinarySearch(array, index, length, value, out retval);
480 if (r)
481 return retval;
483 #endif
485 int lo = index;
486 int hi = index + length - 1;
487 if (array is object[] objArray)
489 while (lo <= hi)
491 // i might overflow if lo and hi are both large positive numbers.
492 int i = GetMedian(lo, hi);
494 int c;
497 c = comparer.Compare(objArray[i], value);
499 catch (Exception e)
501 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
502 return default;
504 if (c == 0) return i;
505 if (c < 0)
507 lo = i + 1;
509 else
511 hi = i - 1;
515 else
517 while (lo <= hi)
519 int i = GetMedian(lo, hi);
521 int c;
524 c = comparer.Compare(array.GetValue(i), value);
526 catch (Exception e)
528 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
529 return default;
531 if (c == 0) return i;
532 if (c < 0)
534 lo = i + 1;
536 else
538 hi = i - 1;
542 return ~lo;
545 public static int BinarySearch<T>(T[] array, T value)
547 if (array == null)
548 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
549 return BinarySearch<T>(array, 0, array.Length, value, null);
552 public static int BinarySearch<T>(T[] array, T value, System.Collections.Generic.IComparer<T>? comparer)
554 if (array == null)
555 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
556 return BinarySearch<T>(array, 0, array.Length, value, comparer);
559 public static int BinarySearch<T>(T[] array, int index, int length, T value)
561 return BinarySearch<T>(array, index, length, value, null);
564 public static int BinarySearch<T>(T[] array, int index, int length, T value, System.Collections.Generic.IComparer<T>? comparer)
566 if (array == null)
567 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
568 if (index < 0)
569 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
570 if (length < 0)
571 ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
573 if (array.Length - index < length)
574 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
576 return ArraySortHelper<T>.Default.BinarySearch(array, index, length, value, comparer);
579 public static TOutput[] ConvertAll<TInput, TOutput>(TInput[] array, Converter<TInput, TOutput> converter)
581 if (array == null)
583 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
586 if (converter == null)
588 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.converter);
591 TOutput[] newArray = new TOutput[array.Length];
592 for (int i = 0; i < array.Length; i++)
594 newArray[i] = converter(array[i]);
596 return newArray;
599 // CopyTo copies a collection into an Array, starting at a particular
600 // index into the array.
602 // This method is to support the ICollection interface, and calls
603 // Array.Copy internally. If you aren't using ICollection explicitly,
604 // call Array.Copy to avoid an extra indirection.
606 public void CopyTo(Array array, int index)
608 if (array != null && array.Rank != 1)
609 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
610 // Note: Array.Copy throws a RankException and we want a consistent ArgumentException for all the IList CopyTo methods.
611 Array.Copy(this, GetLowerBound(0), array!, index, Length);
614 public void CopyTo(Array array, long index)
616 int iindex = (int)index;
617 if (index != iindex)
618 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_HugeArrayNotSupported);
620 this.CopyTo(array, iindex);
623 private static class EmptyArray<T>
625 #pragma warning disable CA1825 // this is the implementation of Array.Empty<T>()
626 internal static readonly T[] Value = new T[0];
627 #pragma warning restore CA1825
630 public static T[] Empty<T>()
632 return EmptyArray<T>.Value;
635 public static bool Exists<T>(T[] array, Predicate<T> match)
637 return Array.FindIndex(array, match) != -1;
640 public static void Fill<T>(T[] array, T value)
642 if (array == null)
644 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
647 for (int i = 0; i < array.Length; i++)
649 array[i] = value;
653 public static void Fill<T>(T[] array, T value, int startIndex, int count)
655 if (array == null)
657 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
660 if (startIndex < 0 || startIndex > array.Length)
662 ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
665 if (count < 0 || startIndex > array.Length - count)
667 ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
670 for (int i = startIndex; i < startIndex + count; i++)
672 array[i] = value;
676 [return: MaybeNull]
677 public static T Find<T>(T[] array, Predicate<T> match)
679 if (array == null)
681 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
684 if (match == null)
686 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
689 for (int i = 0; i < array.Length; i++)
691 if (match(array[i]))
693 return array[i];
696 return default!;
699 public static T[] FindAll<T>(T[] array, Predicate<T> match)
701 if (array == null)
703 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
706 if (match == null)
708 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
711 List<T> list = new List<T>();
712 for (int i = 0; i < array.Length; i++)
714 if (match(array[i]))
716 list.Add(array[i]);
719 return list.ToArray();
722 public static int FindIndex<T>(T[] array, Predicate<T> match)
724 if (array == null)
726 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
729 return FindIndex(array, 0, array.Length, match);
732 public static int FindIndex<T>(T[] array, int startIndex, Predicate<T> match)
734 if (array == null)
736 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
739 return FindIndex(array, startIndex, array.Length - startIndex, match);
742 public static int FindIndex<T>(T[] array, int startIndex, int count, Predicate<T> match)
744 if (array == null)
746 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
749 if (startIndex < 0 || startIndex > array.Length)
751 ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
754 if (count < 0 || startIndex > array.Length - count)
756 ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
759 if (match == null)
761 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
764 int endIndex = startIndex + count;
765 for (int i = startIndex; i < endIndex; i++)
767 if (match(array[i]))
768 return i;
770 return -1;
773 [return: MaybeNull]
774 public static T FindLast<T>(T[] array, Predicate<T> match)
776 if (array == null)
778 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
781 if (match == null)
783 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
786 for (int i = array.Length - 1; i >= 0; i--)
788 if (match(array[i]))
790 return array[i];
793 return default!;
796 public static int FindLastIndex<T>(T[] array, Predicate<T> match)
798 if (array == null)
800 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
803 return FindLastIndex(array, array.Length - 1, array.Length, match);
806 public static int FindLastIndex<T>(T[] array, int startIndex, Predicate<T> match)
808 if (array == null)
810 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
813 return FindLastIndex(array, startIndex, startIndex + 1, match);
816 public static int FindLastIndex<T>(T[] array, int startIndex, int count, Predicate<T> match)
818 if (array == null)
820 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
823 if (match == null)
825 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
828 if (array.Length == 0)
830 // Special case for 0 length List
831 if (startIndex != -1)
833 ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
836 else
838 // Make sure we're not out of range
839 if (startIndex < 0 || startIndex >= array.Length)
841 ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
845 // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
846 if (count < 0 || startIndex - count + 1 < 0)
848 ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
851 int endIndex = startIndex - count;
852 for (int i = startIndex; i > endIndex; i--)
854 if (match(array[i]))
856 return i;
859 return -1;
862 public static void ForEach<T>(T[] array, Action<T> action)
864 if (array == null)
866 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
869 if (action == null)
871 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.action);
874 for (int i = 0; i < array.Length; i++)
876 action(array[i]);
880 // Returns the index of the first occurrence of a given value in an array.
881 // The array is searched forwards, and the elements of the array are
882 // compared to the given value using the Object.Equals method.
884 public static int IndexOf(Array array, object? value)
886 if (array == null)
887 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
888 return IndexOf(array, value, array.GetLowerBound(0), array.Length);
891 // Returns the index of the first occurrence of a given value in a range of
892 // an array. The array is searched forwards, starting at index
893 // startIndex and ending at the last element of the array. The
894 // elements of the array are compared to the given value using the
895 // Object.Equals method.
897 public static int IndexOf(Array array, object? value, int startIndex)
899 if (array == null)
900 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
901 int lb = array.GetLowerBound(0);
902 return IndexOf(array, value, startIndex, array.Length - startIndex + lb);
905 // Returns the index of the first occurrence of a given value in a range of
906 // an array. The array is searched forwards, starting at index
907 // startIndex and upto count elements. The
908 // elements of the array are compared to the given value using the
909 // Object.Equals method.
911 public static int IndexOf(Array array, object? value, int startIndex, int count)
913 if (array == null)
914 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
915 if (array.Rank != 1)
916 ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
918 int lb = array.GetLowerBound(0);
919 if (startIndex < lb || startIndex > array.Length + lb)
920 ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
921 if (count < 0 || count > array.Length - startIndex + lb)
922 ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
924 #if !CORERT
925 // Try calling a quick native method to handle primitive types.
926 int retVal;
927 bool r = TrySZIndexOf(array, startIndex, count, value, out retVal);
928 if (r)
929 return retVal;
930 #endif
932 int endIndex = startIndex + count;
933 if (array is object[] objArray)
935 if (value == null)
937 for (int i = startIndex; i < endIndex; i++)
939 if (objArray[i] == null)
940 return i;
943 else
945 for (int i = startIndex; i < endIndex; i++)
947 object obj = objArray[i];
948 if (obj != null && obj.Equals(value))
949 return i;
953 else
955 for (int i = startIndex; i < endIndex; i++)
957 object? obj = array.GetValue(i);
958 if (obj == null)
960 if (value == null)
961 return i;
963 else
965 if (obj.Equals(value))
966 return i;
970 // Return one less than the lower bound of the array. This way,
971 // for arrays with a lower bound of -1 we will not return -1 when the
972 // item was not found. And for SZArrays (the vast majority), -1 still
973 // works for them.
974 return lb - 1;
977 public static int IndexOf<T>(T[] array, T value)
979 if (array == null)
981 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
984 return IndexOf(array, value, 0, array.Length);
987 public static int IndexOf<T>(T[] array, T value, int startIndex)
989 if (array == null)
991 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
994 return IndexOf(array, value, startIndex, array.Length - startIndex);
997 public static int IndexOf<T>(T[] array, T value, int startIndex, int count)
999 if (array == null)
1001 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1004 if ((uint)startIndex > (uint)array.Length)
1006 ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
1009 if ((uint)count > (uint)(array.Length - startIndex))
1011 ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
1014 if (RuntimeHelpers.IsBitwiseEquatable<T>())
1016 if (Unsafe.SizeOf<T>() == sizeof(byte))
1018 int result = SpanHelpers.IndexOf(
1019 ref Unsafe.Add(ref array.GetRawSzArrayData(), startIndex),
1020 Unsafe.As<T, byte>(ref value),
1021 count);
1022 return (result >= 0 ? startIndex : 0) + result;
1024 else if (Unsafe.SizeOf<T>() == sizeof(char))
1026 int result = SpanHelpers.IndexOf(
1027 ref Unsafe.Add(ref Unsafe.As<byte, char>(ref array.GetRawSzArrayData()), startIndex),
1028 Unsafe.As<T, char>(ref value),
1029 count);
1030 return (result >= 0 ? startIndex : 0) + result;
1032 else if (Unsafe.SizeOf<T>() == sizeof(int))
1034 int result = SpanHelpers.IndexOf(
1035 ref Unsafe.Add(ref Unsafe.As<byte, int>(ref array.GetRawSzArrayData()), startIndex),
1036 Unsafe.As<T, int>(ref value),
1037 count);
1038 return (result >= 0 ? startIndex : 0) + result;
1040 else if (Unsafe.SizeOf<T>() == sizeof(long))
1042 int result = SpanHelpers.IndexOf(
1043 ref Unsafe.Add(ref Unsafe.As<byte, long>(ref array.GetRawSzArrayData()), startIndex),
1044 Unsafe.As<T, long>(ref value),
1045 count);
1046 return (result >= 0 ? startIndex : 0) + result;
1050 #if !CORERT
1051 return EqualityComparer<T>.Default.IndexOf(array, value, startIndex, count);
1052 #else
1053 return IndexOfImpl(array, value, startIndex, count);
1054 #endif
1057 // Returns the index of the last occurrence of a given value in an array.
1058 // The array is searched backwards, and the elements of the array are
1059 // compared to the given value using the Object.Equals method.
1061 public static int LastIndexOf(Array array, object? value)
1063 if (array == null)
1064 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1065 int lb = array.GetLowerBound(0);
1066 return LastIndexOf(array, value, array.Length - 1 + lb, array.Length);
1069 // Returns the index of the last occurrence of a given value in a range of
1070 // an array. The array is searched backwards, starting at index
1071 // startIndex and ending at index 0. The elements of the array are
1072 // compared to the given value using the Object.Equals method.
1074 public static int LastIndexOf(Array array, object? value, int startIndex)
1076 if (array == null)
1077 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1078 int lb = array.GetLowerBound(0);
1079 return LastIndexOf(array, value, startIndex, startIndex + 1 - lb);
1082 // Returns the index of the last occurrence of a given value in a range of
1083 // an array. The array is searched backwards, starting at index
1084 // startIndex and counting uptocount elements. The elements of
1085 // the array are compared to the given value using the Object.Equals
1086 // method.
1088 public static int LastIndexOf(Array array, object? value, int startIndex, int count)
1090 if (array == null)
1091 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1092 int lb = array.GetLowerBound(0);
1093 if (array.Length == 0)
1095 return lb - 1;
1098 if (startIndex < lb || startIndex >= array.Length + lb)
1099 ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
1100 if (count < 0)
1101 ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
1102 if (count > startIndex - lb + 1)
1103 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.endIndex, ExceptionResource.ArgumentOutOfRange_EndIndexStartIndex);
1104 if (array.Rank != 1)
1105 ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
1107 #if !CORERT
1108 // Try calling a quick native method to handle primitive types.
1109 int retVal;
1110 bool r = TrySZLastIndexOf(array, startIndex, count, value, out retVal);
1111 if (r)
1112 return retVal;
1113 #endif
1115 int endIndex = startIndex - count + 1;
1116 if (array is object[] objArray)
1118 if (value == null)
1120 for (int i = startIndex; i >= endIndex; i--)
1122 if (objArray[i] == null)
1123 return i;
1126 else
1128 for (int i = startIndex; i >= endIndex; i--)
1130 object obj = objArray[i];
1131 if (obj != null && obj.Equals(value))
1132 return i;
1136 else
1138 for (int i = startIndex; i >= endIndex; i--)
1140 object? obj = array.GetValue(i);
1141 if (obj == null)
1143 if (value == null)
1144 return i;
1146 else
1148 if (obj.Equals(value))
1149 return i;
1153 return lb - 1; // Return lb-1 for arrays with negative lower bounds.
1156 public static int LastIndexOf<T>(T[] array, T value)
1158 if (array == null)
1160 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1163 return LastIndexOf(array, value, array.Length - 1, array.Length);
1166 public static int LastIndexOf<T>(T[] array, T value, int startIndex)
1168 if (array == null)
1170 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1172 // if array is empty and startIndex is 0, we need to pass 0 as count
1173 return LastIndexOf(array, value, startIndex, (array.Length == 0) ? 0 : (startIndex + 1));
1176 public static int LastIndexOf<T>(T[] array, T value, int startIndex, int count)
1178 if (array == null)
1180 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1183 if (array.Length == 0)
1186 // Special case for 0 length List
1187 // accept -1 and 0 as valid startIndex for compablility reason.
1189 if (startIndex != -1 && startIndex != 0)
1191 ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
1194 // only 0 is a valid value for count if array is empty
1195 if (count != 0)
1197 ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
1199 return -1;
1202 // Make sure we're not out of range
1203 if ((uint)startIndex >= (uint)array.Length)
1205 ThrowHelper.ThrowStartIndexArgumentOutOfRange_ArgumentOutOfRange_Index();
1208 // 2nd have of this also catches when startIndex == MAXINT, so MAXINT - 0 + 1 == -1, which is < 0.
1209 if (count < 0 || startIndex - count + 1 < 0)
1211 ThrowHelper.ThrowCountArgumentOutOfRange_ArgumentOutOfRange_Count();
1214 if (RuntimeHelpers.IsBitwiseEquatable<T>())
1216 if (Unsafe.SizeOf<T>() == sizeof(byte))
1218 int endIndex = startIndex - count + 1;
1219 int result = SpanHelpers.LastIndexOf(
1220 ref Unsafe.Add(ref array.GetRawSzArrayData(), endIndex),
1221 Unsafe.As<T, byte>(ref value),
1222 count);
1224 return (result >= 0 ? endIndex : 0) + result;
1226 else if (Unsafe.SizeOf<T>() == sizeof(char))
1228 int endIndex = startIndex - count + 1;
1229 int result = SpanHelpers.LastIndexOf(
1230 ref Unsafe.Add(ref Unsafe.As<byte, char>(ref array.GetRawSzArrayData()), endIndex),
1231 Unsafe.As<T, char>(ref value),
1232 count);
1234 return (result >= 0 ? endIndex : 0) + result;
1236 else if (Unsafe.SizeOf<T>() == sizeof(int))
1238 int endIndex = startIndex - count + 1;
1239 int result = SpanHelpers.LastIndexOf(
1240 ref Unsafe.Add(ref Unsafe.As<byte, int>(ref array.GetRawSzArrayData()), endIndex),
1241 Unsafe.As<T, int>(ref value),
1242 count);
1244 return (result >= 0 ? endIndex : 0) + result;
1246 else if (Unsafe.SizeOf<T>() == sizeof(long))
1248 int endIndex = startIndex - count + 1;
1249 int result = SpanHelpers.LastIndexOf(
1250 ref Unsafe.Add(ref Unsafe.As<byte, long>(ref array.GetRawSzArrayData()), endIndex),
1251 Unsafe.As<T, long>(ref value),
1252 count);
1254 return (result >= 0 ? endIndex : 0) + result;
1258 #if !CORERT
1259 return EqualityComparer<T>.Default.LastIndexOf(array, value, startIndex, count);
1260 #else
1261 return LastIndexOfImpl(array, value, startIndex, count);
1262 #endif
1265 // Reverses all elements of the given array. Following a call to this
1266 // method, an element previously located at index i will now be
1267 // located at index length - i - 1, where length is the
1268 // length of the array.
1270 public static void Reverse(Array array)
1272 if (array == null)
1273 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1274 Reverse(array, array.GetLowerBound(0), array.Length);
1277 // Reverses the elements in a range of an array. Following a call to this
1278 // method, an element in the range given by index and count
1279 // which was previously located at index i will now be located at
1280 // index index + (index + count - i - 1).
1281 // Reliability note: This may fail because it may have to box objects.
1283 public static void Reverse(Array array, int index, int length)
1285 if (array == null)
1286 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1287 int lowerBound = array.GetLowerBound(0);
1288 if (index < lowerBound)
1289 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1290 if (length < 0)
1291 ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
1293 if (array.Length - (index - lowerBound) < length)
1294 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
1295 if (array.Rank != 1)
1296 ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
1298 if (length <= 1)
1299 return;
1301 #if !CORERT
1302 bool r = TrySZReverse(array, index, length);
1303 if (r)
1304 return;
1305 #endif
1307 if (array is object[] objArray)
1309 Array.Reverse<object>(objArray, index, length);
1311 else
1313 int i = index;
1314 int j = index + length - 1;
1315 while (i < j)
1317 object? temp = array.GetValue(i);
1318 array.SetValue(array.GetValue(j), i);
1319 array.SetValue(temp, j);
1320 i++;
1321 j--;
1326 public static void Reverse<T>(T[] array)
1328 if (array == null)
1329 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1330 Reverse(array, 0, array.Length);
1333 public static void Reverse<T>(T[] array, int index, int length)
1335 if (array == null)
1336 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1337 if (index < 0)
1338 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1339 if (length < 0)
1340 ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
1341 if (array.Length - index < length)
1342 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
1344 if (length <= 1)
1345 return;
1347 ref T first = ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), index);
1348 ref T last = ref Unsafe.Add(ref Unsafe.Add(ref first, length), -1);
1351 T temp = first;
1352 first = last;
1353 last = temp;
1354 first = ref Unsafe.Add(ref first, 1);
1355 last = ref Unsafe.Add(ref last, -1);
1356 } while (Unsafe.IsAddressLessThan(ref first, ref last));
1359 // Sorts the elements of an array. The sort compares the elements to each
1360 // other using the IComparable interface, which must be implemented
1361 // by all elements of the array.
1363 public static void Sort(Array array)
1365 if (array == null)
1366 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1367 Sort(array, null, array.GetLowerBound(0), array.Length, null);
1370 // Sorts the elements of two arrays based on the keys in the first array.
1371 // Elements in the keys array specify the sort keys for
1372 // corresponding elements in the items array. The sort compares the
1373 // keys to each other using the IComparable interface, which must be
1374 // implemented by all elements of the keys array.
1376 public static void Sort(Array keys, Array? items)
1378 if (keys == null)
1379 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
1380 Sort(keys, items, keys.GetLowerBound(0), keys.Length, null);
1383 // Sorts the elements in a section of an array. The sort compares the
1384 // elements to each other using the IComparable interface, which
1385 // must be implemented by all elements in the given section of the array.
1387 public static void Sort(Array array, int index, int length)
1389 Sort(array, null, index, length, null);
1392 // Sorts the elements in a section of two arrays based on the keys in the
1393 // first array. Elements in the keys array specify the sort keys for
1394 // corresponding elements in the items array. The sort compares the
1395 // keys to each other using the IComparable interface, which must be
1396 // implemented by all elements of the keys array.
1398 public static void Sort(Array keys, Array? items, int index, int length)
1400 Sort(keys, items, index, length, null);
1403 // Sorts the elements of an array. The sort compares the elements to each
1404 // other using the given IComparer interface. If comparer is
1405 // null, the elements are compared to each other using the
1406 // IComparable interface, which in that case must be implemented by
1407 // all elements of the array.
1409 public static void Sort(Array array, IComparer? comparer)
1411 if (array == null)
1412 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1413 Sort(array, null, array.GetLowerBound(0), array.Length, comparer);
1416 // Sorts the elements of two arrays based on the keys in the first array.
1417 // Elements in the keys array specify the sort keys for
1418 // corresponding elements in the items array. The sort compares the
1419 // keys to each other using the given IComparer interface. If
1420 // comparer is null, the elements are compared to each other using
1421 // the IComparable interface, which in that case must be implemented
1422 // by all elements of the keys array.
1424 public static void Sort(Array keys, Array? items, IComparer? comparer)
1426 if (keys == null)
1427 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
1428 Sort(keys, items, keys.GetLowerBound(0), keys.Length, comparer);
1431 // Sorts the elements in a section of an array. The sort compares the
1432 // elements to each other using the given IComparer interface. If
1433 // comparer is null, the elements are compared to each other using
1434 // the IComparable interface, which in that case must be implemented
1435 // by all elements in the given section of the array.
1437 public static void Sort(Array array, int index, int length, IComparer? comparer)
1439 Sort(array, null, index, length, comparer);
1442 // Sorts the elements in a section of two arrays based on the keys in the
1443 // first array. Elements in the keys array specify the sort keys for
1444 // corresponding elements in the items array. The sort compares the
1445 // keys to each other using the given IComparer interface. If
1446 // comparer is null, the elements are compared to each other using
1447 // the IComparable interface, which in that case must be implemented
1448 // by all elements of the given section of the keys array.
1450 public static void Sort(Array keys, Array? items, int index, int length, IComparer? comparer)
1452 if (keys == null)
1453 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
1454 if (keys.Rank != 1 || (items != null && items.Rank != 1))
1455 ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
1456 int keysLowerBound = keys.GetLowerBound(0);
1457 if (items != null && keysLowerBound != items.GetLowerBound(0))
1458 ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_LowerBoundsMustMatch);
1459 if (index < keysLowerBound)
1460 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1461 if (length < 0)
1462 ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
1464 if (keys.Length - (index - keysLowerBound) < length || (items != null && (index - keysLowerBound) > items.Length - length))
1465 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
1467 if (length > 1)
1469 SortImpl(keys, items, index, length, comparer ?? Comparer.Default);
1473 public static void Sort<T>(T[] array)
1475 if (array == null)
1476 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1477 Sort<T>(array, 0, array.Length, null);
1480 public static void Sort<TKey, TValue>(TKey[] keys, TValue[]? items)
1482 if (keys == null)
1483 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
1484 Sort<TKey, TValue>(keys, items, 0, keys.Length, null);
1487 public static void Sort<T>(T[] array, int index, int length)
1489 Sort<T>(array, index, length, null);
1492 public static void Sort<TKey, TValue>(TKey[] keys, TValue[]? items, int index, int length)
1494 Sort<TKey, TValue>(keys, items, index, length, null);
1497 public static void Sort<T>(T[] array, System.Collections.Generic.IComparer<T>? comparer)
1499 if (array == null)
1500 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1501 Sort<T>(array, 0, array.Length, comparer);
1504 public static void Sort<TKey, TValue>(TKey[] keys, TValue[]? items, System.Collections.Generic.IComparer<TKey>? comparer)
1506 if (keys == null)
1507 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
1508 Sort<TKey, TValue>(keys, items, 0, keys.Length, comparer);
1511 public static void Sort<T>(T[] array, int index, int length, System.Collections.Generic.IComparer<T>? comparer)
1513 if (array == null)
1514 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1515 if (index < 0)
1516 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1517 if (length < 0)
1518 ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
1519 if (array.Length - index < length)
1520 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
1522 if (length > 1)
1524 #if !CORERT
1525 if (comparer == null || comparer == Comparer<T>.Default)
1527 if (TrySZSort(array, null, index, index + length - 1))
1529 return;
1532 #endif
1534 ArraySortHelper<T>.Default.Sort(array, index, length, comparer);
1538 public static void Sort<TKey, TValue>(TKey[] keys, TValue[]? items, int index, int length, System.Collections.Generic.IComparer<TKey>? comparer)
1540 if (keys == null)
1541 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
1542 if (index < 0)
1543 ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
1544 if (length < 0)
1545 ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
1546 if (keys.Length - index < length || (items != null && index > items.Length - length))
1547 ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
1549 if (length > 1)
1551 #if !CORERT
1552 if (comparer == null || comparer == Comparer<TKey>.Default)
1554 if (TrySZSort(keys, items, index, index + length - 1))
1556 return;
1559 #endif
1561 if (items == null)
1563 Sort<TKey>(keys, index, length, comparer);
1564 return;
1567 ArraySortHelper<TKey, TValue>.Default.Sort(keys, items, index, length, comparer);
1571 public static void Sort<T>(T[] array, Comparison<T> comparison)
1573 if (array == null)
1575 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1578 if (comparison == null)
1580 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison);
1583 ArraySortHelper<T>.Sort(array, 0, array.Length, comparison!);
1586 public static bool TrueForAll<T>(T[] array, Predicate<T> match)
1588 if (array == null)
1590 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
1593 if (match == null)
1595 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
1598 for (int i = 0; i < array.Length; i++)
1600 if (!match(array[i]))
1602 return false;
1605 return true;
1608 #if !CORERT
1609 // Private value type used by the Sort methods.
1610 private readonly struct SorterObjectArray
1612 private readonly object[] keys;
1613 private readonly object?[]? items;
1614 private readonly IComparer comparer;
1616 internal SorterObjectArray(object[] keys, object?[]? items, IComparer comparer)
1618 this.keys = keys;
1619 this.items = items;
1620 this.comparer = comparer;
1623 internal void SwapIfGreaterWithItems(int a, int b)
1625 if (a != b)
1627 if (comparer.Compare(keys[a], keys[b]) > 0)
1629 object temp = keys[a];
1630 keys[a] = keys[b];
1631 keys[b] = temp;
1632 if (items != null)
1634 object? item = items[a];
1635 items[a] = items[b];
1636 items[b] = item;
1642 private void Swap(int i, int j)
1644 object t = keys[i];
1645 keys[i] = keys[j];
1646 keys[j] = t;
1648 if (items != null)
1650 object? item = items[i];
1651 items[i] = items[j];
1652 items[j] = item;
1656 internal void Sort(int left, int length)
1658 IntrospectiveSort(left, length);
1661 private void IntrospectiveSort(int left, int length)
1663 if (length < 2)
1664 return;
1668 IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(length));
1670 catch (IndexOutOfRangeException)
1672 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
1674 catch (Exception e)
1676 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
1680 private void IntroSort(int lo, int hi, int depthLimit)
1682 while (hi > lo)
1684 int partitionSize = hi - lo + 1;
1685 if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
1687 if (partitionSize == 1)
1689 return;
1691 if (partitionSize == 2)
1693 SwapIfGreaterWithItems(lo, hi);
1694 return;
1696 if (partitionSize == 3)
1698 SwapIfGreaterWithItems(lo, hi - 1);
1699 SwapIfGreaterWithItems(lo, hi);
1700 SwapIfGreaterWithItems(hi - 1, hi);
1701 return;
1704 InsertionSort(lo, hi);
1705 return;
1708 if (depthLimit == 0)
1710 Heapsort(lo, hi);
1711 return;
1713 depthLimit--;
1715 int p = PickPivotAndPartition(lo, hi);
1716 IntroSort(p + 1, hi, depthLimit);
1717 hi = p - 1;
1721 private int PickPivotAndPartition(int lo, int hi)
1723 // Compute median-of-three. But also partition them, since we've done the comparison.
1724 int mid = lo + (hi - lo) / 2;
1725 // Sort lo, mid and hi appropriately, then pick mid as the pivot.
1726 SwapIfGreaterWithItems(lo, mid);
1727 SwapIfGreaterWithItems(lo, hi);
1728 SwapIfGreaterWithItems(mid, hi);
1730 object pivot = keys[mid];
1731 Swap(mid, hi - 1);
1732 int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
1734 while (left < right)
1736 while (comparer.Compare(keys[++left], pivot) < 0) ;
1737 while (comparer.Compare(pivot, keys[--right]) < 0) ;
1739 if (left >= right)
1740 break;
1742 Swap(left, right);
1745 // Put pivot in the right location.
1746 Swap(left, hi - 1);
1747 return left;
1750 private void Heapsort(int lo, int hi)
1752 int n = hi - lo + 1;
1753 for (int i = n / 2; i >= 1; i--)
1755 DownHeap(i, n, lo);
1757 for (int i = n; i > 1; i--)
1759 Swap(lo, lo + i - 1);
1761 DownHeap(1, i - 1, lo);
1765 private void DownHeap(int i, int n, int lo)
1767 object d = keys[lo + i - 1];
1768 object? dt = items?[lo + i - 1];
1769 int child;
1770 while (i <= n / 2)
1772 child = 2 * i;
1773 if (child < n && comparer.Compare(keys[lo + child - 1], keys[lo + child]) < 0)
1775 child++;
1777 if (!(comparer.Compare(d, keys[lo + child - 1]) < 0))
1778 break;
1779 keys[lo + i - 1] = keys[lo + child - 1];
1780 if (items != null)
1781 items[lo + i - 1] = items[lo + child - 1];
1782 i = child;
1784 keys[lo + i - 1] = d;
1785 if (items != null)
1786 items[lo + i - 1] = dt;
1789 private void InsertionSort(int lo, int hi)
1791 int i, j;
1792 object t;
1793 object? ti;
1794 for (i = lo; i < hi; i++)
1796 j = i;
1797 t = keys[i + 1];
1798 ti = items?[i + 1];
1799 while (j >= lo && comparer.Compare(t, keys[j]) < 0)
1801 keys[j + 1] = keys[j];
1802 if (items != null)
1803 items[j + 1] = items[j];
1804 j--;
1806 keys[j + 1] = t;
1807 if (items != null)
1808 items[j + 1] = ti;
1813 // Private value used by the Sort methods for instances of Array.
1814 // This is slower than the one for Object[], since we can't use the JIT helpers
1815 // to access the elements. We must use GetValue & SetValue.
1816 private readonly struct SorterGenericArray
1818 private readonly Array keys;
1819 private readonly Array? items;
1820 private readonly IComparer comparer;
1822 internal SorterGenericArray(Array keys, Array? items, IComparer comparer)
1824 this.keys = keys;
1825 this.items = items;
1826 this.comparer = comparer;
1829 internal void SwapIfGreaterWithItems(int a, int b)
1831 if (a != b)
1833 if (comparer.Compare(keys.GetValue(a), keys.GetValue(b)) > 0)
1835 object? key = keys.GetValue(a);
1836 keys.SetValue(keys.GetValue(b), a);
1837 keys.SetValue(key, b);
1838 if (items != null)
1840 object? item = items.GetValue(a);
1841 items.SetValue(items.GetValue(b), a);
1842 items.SetValue(item, b);
1848 private void Swap(int i, int j)
1850 object? t1 = keys.GetValue(i);
1851 keys.SetValue(keys.GetValue(j), i);
1852 keys.SetValue(t1, j);
1854 if (items != null)
1856 object? t2 = items.GetValue(i);
1857 items.SetValue(items.GetValue(j), i);
1858 items.SetValue(t2, j);
1862 internal void Sort(int left, int length)
1864 IntrospectiveSort(left, length);
1867 private void IntrospectiveSort(int left, int length)
1869 if (length < 2)
1870 return;
1874 IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2PlusOne(length));
1876 catch (IndexOutOfRangeException)
1878 IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(comparer);
1880 catch (Exception e)
1882 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_IComparerFailed, e);
1886 private void IntroSort(int lo, int hi, int depthLimit)
1888 while (hi > lo)
1890 int partitionSize = hi - lo + 1;
1891 if (partitionSize <= IntrospectiveSortUtilities.IntrosortSizeThreshold)
1893 if (partitionSize == 1)
1895 return;
1897 if (partitionSize == 2)
1899 SwapIfGreaterWithItems(lo, hi);
1900 return;
1902 if (partitionSize == 3)
1904 SwapIfGreaterWithItems(lo, hi - 1);
1905 SwapIfGreaterWithItems(lo, hi);
1906 SwapIfGreaterWithItems(hi - 1, hi);
1907 return;
1910 InsertionSort(lo, hi);
1911 return;
1914 if (depthLimit == 0)
1916 Heapsort(lo, hi);
1917 return;
1919 depthLimit--;
1921 int p = PickPivotAndPartition(lo, hi);
1922 IntroSort(p + 1, hi, depthLimit);
1923 hi = p - 1;
1927 private int PickPivotAndPartition(int lo, int hi)
1929 // Compute median-of-three. But also partition them, since we've done the comparison.
1930 int mid = lo + (hi - lo) / 2;
1932 SwapIfGreaterWithItems(lo, mid);
1933 SwapIfGreaterWithItems(lo, hi);
1934 SwapIfGreaterWithItems(mid, hi);
1936 object? pivot = keys.GetValue(mid);
1937 Swap(mid, hi - 1);
1938 int left = lo, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
1940 while (left < right)
1942 while (comparer.Compare(keys.GetValue(++left), pivot) < 0) ;
1943 while (comparer.Compare(pivot, keys.GetValue(--right)) < 0) ;
1945 if (left >= right)
1946 break;
1948 Swap(left, right);
1951 // Put pivot in the right location.
1952 Swap(left, hi - 1);
1953 return left;
1956 private void Heapsort(int lo, int hi)
1958 int n = hi - lo + 1;
1959 for (int i = n / 2; i >= 1; i--)
1961 DownHeap(i, n, lo);
1963 for (int i = n; i > 1; i--)
1965 Swap(lo, lo + i - 1);
1967 DownHeap(1, i - 1, lo);
1971 private void DownHeap(int i, int n, int lo)
1973 object? d = keys.GetValue(lo + i - 1);
1974 object? dt = items?.GetValue(lo + i - 1);
1975 int child;
1976 while (i <= n / 2)
1978 child = 2 * i;
1979 if (child < n && comparer.Compare(keys.GetValue(lo + child - 1), keys.GetValue(lo + child)) < 0)
1981 child++;
1984 if (!(comparer.Compare(d, keys.GetValue(lo + child - 1)) < 0))
1985 break;
1987 keys.SetValue(keys.GetValue(lo + child - 1), lo + i - 1);
1988 if (items != null)
1989 items.SetValue(items.GetValue(lo + child - 1), lo + i - 1);
1990 i = child;
1992 keys.SetValue(d, lo + i - 1);
1993 if (items != null)
1994 items.SetValue(dt, lo + i - 1);
1997 private void InsertionSort(int lo, int hi)
1999 int i, j;
2000 object? t;
2001 object? dt;
2002 for (i = lo; i < hi; i++)
2004 j = i;
2005 t = keys.GetValue(i + 1);
2006 dt = items?.GetValue(i + 1);
2008 while (j >= lo && comparer.Compare(t, keys.GetValue(j)) < 0)
2010 keys.SetValue(keys.GetValue(j), j + 1);
2011 if (items != null)
2012 items.SetValue(items.GetValue(j), j + 1);
2013 j--;
2016 keys.SetValue(t, j + 1);
2017 if (items != null)
2018 items.SetValue(dt, j + 1);
2023 public IEnumerator GetEnumerator()
2025 int lowerBound = GetLowerBound(0);
2026 if (Rank == 1 && lowerBound == 0)
2027 return new SZArrayEnumerator(this);
2028 else
2029 return new ArrayEnumerator(this, lowerBound, Length);
2031 #endif // !CORERT