Back to Blog
C#

C# Array IndexOf: Find Element Positions

Learn how to use C# Array.IndexOf to find element positions, handle missing values, and compare it with LINQ alternatives.

C#ArrayIndexOfLINQ.NET
Illustration of a magnifying glass over an array of boxes with a highlighted index position, representing C# Array.IndexOf.

Array.IndexOf is the direct way to find the position of an element in a one-dimensional C# array. It is a static method that returns the zero-based index of the first occurrence of a specified value. For example, given an array of integers, you can locate the first position of the number 42 with a single call:

int[] numbers = { 10, 20, 42, 30, 42 }; int index = Array.IndexOf(numbers, 42); Console.WriteLine(index); // Output: 2

The method returns -1 when the value is not found. This behavior makes it a straightforward building block for conditional logic, such as checking whether an element exists before performing a removal or update.

Using Array.IndexOf for Value Types

Array.IndexOf works with value types like int, double, bool, and struct instances. For these types, the method uses the default equality comparer, which for simple value types performs a field-by-field comparison. This means you do not need to implement any custom equality logic when the values are simple and directly comparable.

char[] letters = { 'a', 'b', 'c', 'd' }; int position = Array.IndexOf(letters, 'c'); Console.WriteLine(position); // Output: 2

For custom structs, the default comparison checks each field. If your struct contains reference-type fields, the comparison uses reference equality for those fields unless you override Equals. In practice, most value-type searches work as expected without extra setup.

Handling Reference Types and Custom Equality

When the array holds reference types, Array.IndexOf uses object.Equals to compare elements. For strings, this means ordinal comparison by default, which is case-sensitive. Array.IndexOf does not have an overload that accepts an IEqualityComparer, so for case-insensitive search, use Array.FindIndex with a predicate:

string[] names = { "Alice", "Bob", "Carol" }; int index = Array.IndexOf(names, "bob"); // -1 because comparison is case-sensitive int caseInsensitiveIndex = Array.FindIndex(names, name => string.Equals(name, "bob", StringComparison.OrdinalIgnoreCase)); Console.WriteLine(caseInsensitiveIndex); // Output: 1

For custom classes, the default equality is reference equality unless you override Equals and GetHashCode. To search based on a property value, implement IEquatable<T> on the class and override Equals/GetHashCode, or use Array.FindIndex with a predicate. The predicate keeps the comparison logic separate from the class definition and is often simpler.

Searching for Missing Elements and Return Values

The return value of -1 is the canonical way to indicate that the element is not present. This is consistent across all overloads. When you call Array.IndexOf with a starting index and a count, the method searches only that subrange and returns -1 if the value is not found within it.

int[] data = { 5, 7, 5, 9, 5 }; int first = Array.IndexOf(data, 5); // 0 int second = Array.IndexOf(data, 5, 1); // 2 int third = Array.IndexOf(data, 5, 3, 2); // -1, because only indices 3 and 4 are searched

This subrange search is useful when you need to find the next occurrence after a known position. You can loop over an array to collect all indices of a particular value by updating the start index to foundIndex + 1 each time.

Array.IndexOf vs. LINQ: When to Use Which

LINQ provides several ways to find an index, such as using Select with FirstOrDefault, or ToList().IndexOf(). However, Array.IndexOf is more direct and typically more efficient because it avoids the overhead of creating intermediate collections or delegate invocations.

// LINQ approach int linqIndex = numbers .Select((value, idx) => new { value, idx }) .FirstOrDefault(x => x.value == 42)?.idx ?? -1; // Array.IndexOf approach int directIndex = Array.IndexOf(numbers, 42);

The LINQ version allocates an anonymous object for each element and requires a delegate call per item. For small arrays the difference is negligible, but for large arrays or performance-critical loops, Array.IndexOf is the better choice. Use LINQ when you need to combine the index search with other query operations, such as filtering or projecting, in a single expression.

Performance Characteristics and Memory Behavior

Array.IndexOf performs a linear scan from the start index to the end of the specified range. Its time complexity is O(n) in the worst case. The method is implemented in the .NET runtime and does not allocate any additional memory for the search itself. This makes it predictable and suitable for real-time or resource-constrained environments.

For sorted arrays, a binary search would be faster, but Array.IndexOf does not assume any ordering. If you repeatedly search the same large array, consider sorting it and using Array.BinarySearch, which has O(log n) complexity. However, the sorting cost itself may outweigh the benefit unless the array remains sorted across many searches.

Another performance consideration is the equality comparer. For value types, the default comparison is fast because the runtime can specialize it. For reference types, the default object.Equals can be slower, especially if the type overrides Equals with complex logic. Array.IndexOf always uses the default comparer for the element type, so if you need custom equality, use Array.FindIndex or LINQ instead. Measure before optimizing.

Working with Multidimensional and Jagged Arrays

Array.IndexOf works only with one-dimensional arrays. For multidimensional arrays, you need to iterate manually or use LINQ to flatten the array. A common pattern is to loop through each dimension and track the matching position.

int[,] matrix = { { 1, 2 }, { 3, 4 } }; int target = 4; bool found = false; int row = -1, col = -1; for (int i = 0; i < matrix.GetLength(0); i++) { for (int j = 0; j < matrix.GetLength(1); j++) { if (matrix[i, j] == target) { row = i; col = j; found = true; break; } } if (found) break; }

Jagged arrays (arrays of arrays) are arrays of references, so Array.IndexOf on the outer array compares references, not the contents of the inner arrays. To search for a specific element inside a jagged array, you must iterate over the inner arrays and call Array.IndexOf on each one.

Common Pitfalls and Edge Cases

One common mistake is forgetting that Array.IndexOf returns -1 when the value is not found, and then using that result as an index without checking. This can cause an IndexOutOfRangeException if you attempt to access the array at that position.

Another pitfall is assuming that Array.IndexOf works with multidimensional arrays. It does not; it throws a RankException if you pass a multidimensional array to the non-generic overload. Always flatten the array or use a loop for such cases.

For arrays containing null elements, Array.IndexOf searches for null as a valid value. The following code returns the index of the first null element:

string[] items = { "apple", null, "banana" }; int nullIndex = Array.IndexOf(items, null); Console.WriteLine(nullIndex); // Output: 1

This behavior is useful when you need to locate empty slots in a sparse collection, but be careful when the array is expected to have no null values.

Floating-point NaN is handled by double.Equals, which treats NaN as equal to itself. This means Array.IndexOf can locate a double.NaN value in an array. If you need to find all NaN elements or use a custom definition of equality, iterate manually and use double.IsNaN.

Finally, the overloads that accept a starting index and count are subject to argument validation. If the start index is negative or the count extends beyond the array bounds, the method throws ArgumentOutOfRangeException. Always ensure the subrange is valid before calling these overloads, especially when the start index is derived from user input or a previous search result.

C# Array.IndexOf: Practical Usage and Code Examples | RYUSLOG DEV