Deep Dive Into Array Basics - Arrays and Utility Methods
Let's talk about how an Array class is bundled with numerous utility methods to perform common tasks like copying, sorting, searching items, indexing, etc.
Feb 8, 2019 • 11 Minute Read
Introduction
The previous guide, Deep Dive into Array Basics Part 1, I covered the fundamentals of using arrays to manage data. An array provides a simple and effective way to store data, but just storing data is not enough. Consider a billing application; often a requirement is to sort the data in descending order so that a customer can pay attention to the most expensive items first.
Arrays are a reference type, meaning they are type of Array class, therefore Array is the base/abstract class of every array object. An Array class is bundled with numerous utility methods to perform common tasks like copying, sorting, searching items, indexing, etc. In this guide, we will explore Array class and its helpful methods.
Array Class and Methods
An Array class is an abstract class and part of the System namespace. abstract class means that the object of an array class cannot be created using a new operator; instead you can use CreateInstance.
CreateInstance
- CreateInstance: method used to construct an array.
Array carArray = Array.CreateInstance(typeof(String), 5);
To create an array object we need:
- Type: data that can be obtained either using typeof operator or using GetType() method .
typeof(int);
"PS".GetType();
Type.GetType("System.String"); // using (namespace + class) as string value
- Length: The size of the array to store specific amount of item(s).
Only a static method can be invoked using name of the class. Hence CreateInstance static method is invoked using class-name, i.e. Array.
SetValue
- SetValue: To set a value in an array at a specific index.
carArray.SetValue("Tesla", 0);
carArray.SetValue("Waymo", 1);
carArray.SetValue("GM", 2);
carArray.SetValue("Uber", 3);
carArray.SetValue("Ford", 4);
GetValue
- GetValue: To get the value from array with an index.
for(var i=0 ;i < carArray.Length; i++){
Console.Write("{0} ",carArray.GetValue(i));
}
// output: Tesla Waymo GM Uber Ford
Note: It is also possible to convert Array class object into native type array declaration using explicit casting.
string[] arr = (string[])carArray;
Console.WriteLine("[{0}]", string.Join(", ", arr)); // [Tesla, Waymo, GM, Uber, Ford]
Sort
- Sort: To sort array in natural order (Ascending for numerics and Lexicographical for string values).
Array.Sort(carArray); //output: Ford GM Tesla Uber Waymo
There are several overloaded methods available for sort and many others to customize the behavior. We can also sort the array in reverse order by passing IComparer object to a overloaded version of sort method:
Array.Sort(carArray, new ReverserIComparerOrder()); // output: Waymo Uber Tesla GM Ford
// ReverserIComparerOrder class
public class ReverserIComparerOrder : IComparer
{
int IComparer.Compare(Object x, Object y)
{
return ((new CaseInsensitiveComparer()).Compare(y, x)); // compare y with x instead of x with y
}
}
The sorting algorithm depends upon the number of inputs:
-
If the partition size is fewer than 16 elements, it uses an insertion sort algorithm.
-
If the number of partitions exceeds 2 * Log ^ N, where N is the range of the input array, it uses a Heapsort algorithm.
-
Otherwise, it uses a Quicksort algorithm.
Search
Search: Arrays class provides BinarySearch method which is based on Binary search algorithm which will return the index value of the searched item.
Binary search is only applicable for sorted arrays, so an array must be sorted before applying binary search.
int index = Array.BinarySearch(carArray, "Tesla"); //output: 2
BinarySearch method will return a negative value if an element is not present in an array.
Clear and Empty Arrays
- Clear: Will reset all the values of an array element to their default value. For string, the default value is null.
Array.Clear(Array, start index, number of elements to clear)
Array.Clear(carArray, 2, 2); // output: [Tesla,Waymo,GM,,]
- Empty: When no valid return type of an array is available, then return Array.Empty<Type>() because it can avoid the clutter of null check or potential exceptions. To return an empty string array:
Array.Empty<string>(); // here string can be replaced with reference class or value datatype name
ConvertAll
- ConvertAll: To convert one type of array into another. The below code will convert the string type marks into int.
string[] marksOf3 = {"70", "90", "85"};
int[] intMarks = Array.ConvertAll(marksOf3, new Converter<string,int>(int.Parse));
// int.Parse is a method name which converts a string to int
Copy
- Copy: Array length is fixed, though a larger array can be created from a smaller one. The source array will be copied into larger array to store more values.
string[] marksOf3 = {"70", "90", "85"};
string[] marksOf5 = new string[5];
Array.Copy(marksOf3,marksOf5,marksOf3.Length);// marksOf5 = [70,90,85,,] : now with two free places
Find or FindAll
- Find or FindAll: To get single value or an array, after applying a condition.
Array.Find(intMarks, mark => mark>80); // output: 90
Array.FindAll(intMarks, mark => mark>80); // output: [90,85]
There are overloaded methods to find the index either from left or right side of array like IndexOf, LasrIndexOf
Resize
- Resize: To manipulate the size of an array. Internally, it will create a larger array (of desired value) and perform the copy operation.
Array.Resize<string>(ref marksOf3, 10); // read previous article for ref
Reverse
- Reverse: To reverse the sequence
Array.Reverse(carArray);
Generic Array
Generic provides the ability to handle any type of data or when data type is unknown while writing code. You can use Type object to create array of a particular type e.g. string in Array.CreateInstance(typeof(String), 5). Type is a class which stores the metadata about applicable methods, max/min value, memory information, etc., which are required to create objects at runtime and perform operations on them. A small example is to create a method to print elements of one dimensional array:
static void PrintArray<T>(T[] data){
Console.WriteLine("[{0}]", String.Join(",", data));
}
PrintArray(new string[]{"P", "S"}); // output: [P,S]
PrintArray(new int[]{1,2,3,4}); //output: [1,2,3,4]
Although you cannot perform any specific operation like addition to all elements because type will be resolved at compile time and will be replaced with a specific type, hence generic are only compile time. An appropriate substitution or code will be generated while resolving generic relations at compile time.
A generic array can also be created using generic parameter:
public class GenericArray<T> {
private T[] array;
private int index = 0;
public GenericArray(int size) {
array = new T[size + 1];
}
public T GetItem(int index) {
return array[index];
}
public void SetItem(T item, int index){
if(index >= 0 && index < array.Length) // verify, index is neither negative nor greater than array capacity
array[index] = item;
}
public void AddItem(T item){
if(index < array.Length-1) // verify, array is not full
array[index++] = item;
}
}
// Usage
GenericArray<int> ints = new GenericArray<int>(5);
ints.AddItem(5);
ints.AddItem(3);
Console.WriteLine(ints.GetItem(1)); // 3
GenericArray<string> initials = new GenericArray<string>(5);
initials.AddItem("P");
initials.AddItem("S");
Console.WriteLine(initials.GetItem(1)); // S
Some benefits of generics are type safety, code re-usability, and avoid using Object type with casting.
Thread Safety
Threads are sub-processes and are used to break a large process into smaller sub-processes while executing them simultaneously (also dependent on hardware). Like generating the sum of a large series by splitting the series into sub-series and adding the result of all sub-series.
Thread safety is related to the write/modify operation of data. If multiple threads can change the data at the same time then this can lead to unexpected changes in data, but read operations will have no side-effects. A locking mechanism can be used to synchronize the access meaning that only one process at a time (which has its write lock activated) can modify the data.
lock(carArray.SyncRoot){
foreach (Object item in carArray)
Console.WriteLine(item);
}
foreach is a loop to traverse the whole array. It is possible that other processes may try to modify the carArray while traversing an operation. This will cause an InvalidOperationException exception so lock will prevent carArray from being accessed by other processes.
Properties
Array properties like IsFixedSize, IsReadOnly, IsSynchronized will always return fix values as true, false and false respectively because they are constant values and used by the sub-classes of an array, specifically for collection framework implementation like ArrayList etc.
- Rank: a property which describes the dimension of an array.
Console.WriteLine(new int[2,2]{ {1,2},{3,4} }.Rank); // Rank is 2 because it's a two dimensional array
- Length and LongLength return the count of total elements in the array as 32bit, 64bit int value respectively.
Note
- Starting with the .NET Framework 2.0, the Array class implements the System.Collections.Generic.IList, System.Collections.Generic.ICollection, and System.Collections.Generic.IEnumerable generic interfaces but there is no generic information present at compile time. Array object can be explicitly cast to generic list types:
IList<string> list = (IList<string>)carArray;
//or
char[] book = new[]{ 'W' , 'X' , 'Y' , 'Z' };
IList<char> bookList = book;
but any attempt to invoke add, insert and remove
list.Add("Gizmo");
This will result in NotSupportedException: an exception; considered as bad practice.
I hope you found this guide informative and engaging. Share your appreciation and press like on this guide. Thank you for reading!