Data management is the backbone of all applications. The performance of an application is significantly dependent upon the choice of data structure to access and manipulate data. Every data structure has its own implementation and implications.
In earlier times, CPU's were specifically optimized to work with instruction sets that operate on one-dimensional data, known as vector processing. Modern GPUs are a modified version of a vector processor to support rapid computation.
Arrays (AKA Vectors) are the most important and widely used data-structure and this guide will cover the deep aspects of array data-structure.
An array is defined as a collection of similar data with fixed length, stored in a linear fashion. Every element in an array is accessed by an index (a numerical value) and every index can be computed by applying a mathematical operation.
In the below example
1char[] book = {'A','B','C','D'};
Declaration is a process of defining a place holder name preceded by it's type and arrays are declared with rectangular brackets as
1type[] name_of_placeholder;
2char[] book;
Initialization means to allocate memory to objects with the help of new
keyword or array initializer.
1name_of_placeholder = new data_type[size_of_array];
2book = new char[4];
Array Initializers: Arrays can also be declared using initialization list {array elements separated by comma}
and suitable when you have the data while declaring array reference. e.g. create array of length 4 with values as A, B, C, D.
1char[] array = new char[] {'A','B','C','D'};
2char[] array = {'A','B','C','D'};
3char[] array = new[] {'A','B','C','D'};
Implicit typed array: var
is the variable type placeholder (introduced in C# 3) whose type can be obtained from the right side of the expression using type inference which allows us to eliminate the explicit data type. The above examples can be declared using var
as:
1var array = new char[4]; // creates array of length 4
2var array = new char[4] {'A','B','C','D'};
3var array = new char[] {'A','B','C','D'};
4var array = new[] {'A','B','C','D' };
An array initializer cannot be used with the var
keyword, so var array = { 'A', 'B' , 'C', 'D' };
is invalid. The decision was made by the design team to avoid the use of array initializer and its side effects on the parser to avoid parsing nested blocks of {}
. The solution was the usage of new
keyword with var for implicit types syntax.
The main memory is divided into separate logical sections and a heap is known as dynamic memory. Meaning that memory requested at runtime is allocated in heap.
Memory for arrays is allocated in a continuous manner. Meaning that if you create an array of four char
elements then the application will allocate:
On 32 bit OS, char
is of 2 byte and 4 byte for reference variable so (2 * 4) + 4 = 12 bytes
On 64 bit OS, char
is of 2 byte and 8 byte for reference variable so (2 * 4) + 8 = 16 bytes
and may be stored at memory address 2000 to 2008 (2008 is exclusive), look like this:
1index Data Memory addresses
2 _____
30 | A | 4000 <= name of the array
4 |_____| pointing to initial address
51 | B | 4002
6 |_____|
72 | C | 4004
8 |_____|
93 | D | 4006
10 |_____|
This is a general scenario, the actual allocation might slightly differ according to OS implementation.
The value of an array is read by using the array name followed by index within rectangular brackets:
1array_name[numeric_index]
2e.g output
3book[2] C
The memory address of C
can be computed using initial address of memory, index and size:
1initial address + (index * size of data type)
24000 + (2 * 2)
34000 + 4
4=> 4004
In order to modify array elements, an assignment(=) operator is used along with the array name and index:
1array_name[numeric_index] = new_element
2book[0] = 'P' // Replace A with P
A modified book array will be:
1['P','B','C','D'];
Important: There are two types of data in C#:
Value Type: ValueType elements always have a fixed default value when initialized e.g. The default value of an int
element is 0
and bool
is false
.
null
.Zero-based numbering: Array index always starts at 0 and the value of the last index will be length_of_array - 1
.
Fix length: Once an array is created, its size cannot be altered to increase the length. A larger array is required to store the values from the smaller array using a copy operation.
By default, every element in array is initialized to their default value.
Every array object is derived from Array
class and inherits the data member (length,Rank) and methods (GetLowerBound, GetUpperBound) of the Array class.
An array can also represent matrix data as a two dimensional, multi-dimensional, or jagged array. In a jagged array, the size of each row can be different and can be defined at run time as per requirement.
The default maximum size of an Array is two gigabytes (GB) in a 32 bit environment and in a 64 bit environment, gcAllowVeryLargeObjects method can be invoked to expand the limit to four billion entries.
Every array variable has Array
class as the base type and can access the properties and methods. In C or C++, you cannot get the array length from the array object, so being an object type of a class allows array objects to store additional information like length, IsReadOn, and helpful methods like Clone and Equals.
Clone
will provide a shallow copy which is suitable for one dimensional arrays. For a multi-dimensional array, you need to copy each and every element from the source array to the newarray. This is known as deep copy.
Array class: An array class implements various methods from interfaces to provide a fixed set of features for consistency across any supported framework and platform.
using System.Linq
which is used to convert data from one type to another.1using System.Linq;
2
3int[] intArr = new [] { 11, 2, 0, 14, 112 };
4List<int> intList = intArr.OfType<int>().ToList(); // convert array to List
1int[] arr = new int[2];
2int[] temp = (int[])arr.Clone();
1StructuralComparisons.StructuralEqualityComparer.Equals(new int[]{1,2}, new int[]{2,1}); // False
2
3StructuralComparisons.StructuralEqualityComparer.Equals(new int[]{1,2}, new int[]{1,2}); // True
sort
to define the order or create customize ordering.1StructuralComparisons.StructuralComparer.Compare(new int[]{1,2}, new int[]{2,1}); // -1 first comes before in a second
2
3StructuralComparisons.StructuralComparer.Compare(new int[]{1,2}, new int[]{1,2}); // 0 both are equals
4
5StructuralComparisons.StructuralEqualityComparer.Equals(new int[]{2,1}, new int[]{1,2}); // 1, first comes after second
Traversal is the process of accessing each element of an array to perform a read or update operation. To print the book
array, you can write:
1Console.WriteLine("["+book[0]+","+book[1]+","+book[2]+","+book[3]+"]"); // direct access
2Console.WriteLine("[{0},{1},{2},{3}]",book[0],book[1],book[2],book[3]); // interpolated string
3Console.WriteLine("[{0}]", string.Join(",", book)); // get array string and use comma as separator
4Output:
5[A,B,C,D] // for all 3 statements
As you can observe, the only dynamic part to access the array is the index value. So, we just need to execute the book[index_value]
statement with an index value in an increasing order. This can be achieved by simply using for
loop.
Loops allow us to execute the same set of instructions several times in a defined sequence. A simple for
loop syntax is:
1for(int i = initial_value ; exit_condtion ; increment/decrement){
2 // statement
3}
Below is a simple program to replace the value of book
array with any random alphabet:
1Random rand = new Random();
2string strA_Z = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
3for(int i = 0 ; i < book.Length ; i++){
4 // Note : FormattableString string instance is reused to display new values
5 Console.Write("book[{0}] = {1}",i,book[i]);
6 // random number between 0 - 25
7 int randomOndex = rand.Next(strA_Z.Length);
8 // replace element with random alphabet
9 book[i] = strA_Z[randomOndex];
10 Console.WriteLine(" : book[{0}] = {1}",i,book[i]);
11}
12Output: Its random, so output may differ
13book[0] = A : book[0] = H
14book[1] = B : book[1] = U
15book[2] = C : book[2] = G
16book[3] = D : book[3] = O
Note: String objects can be used as an array because of inbuilt indexer support in string class.
Arrays can be passed from one method to another as a parameter to process array data. Passing an array as a parameter means the callee method can only access or manipulate the array data but cannot assign a new array object to the passed array reference.
Note: Caller is the method that calls another method - e.g. Main is a caller and ChangeReference is a callee method, invoked by Main method.
For example:
1static void Main(string[] args){
2 char[] book = new[]{'A','B','C','D'};
3 Console.WriteLine("[{0}]",string.Join(",",book)); // [A,B,C,D]
4 ChangeReference(book); // no change after this executes
5 Console.WriteLine("[{0}]",string.Join(",",book)); // [A,B,C,D]
6}
7static void ChangeReference(char[] book){
8 Console.WriteLine("[{0}]",string.Join(",",book)); // [A,B,C,D]
9 book = new[]{'W','X','Y','Z'};
10}
In the above example, the book
array is passed to another method where a new array object is assigned to the book
reference. Though this will not change the original book
reference pointing to A,B,C,D
, the ChangeReference
method can access or modify the elements inside of the book
array.
There is the possibility that an uninitialized array could be passed or that a method needs to initialize the array reference before using it and make the changes that reflect in the caller method. Fortunately, C# introduced keywords like ref
and out
in version 7 to apply constrains on references at compilation time.
out
indicates to the compiler that the received array reference (in callee method) should be initialized before use and if you try to use the array reference before initialization then it will cause a compilation error.
1static void Main(string[] args){
2 char[] book = new[]{'A','B','C','D'};
3 Console.WriteLine("[{0}]",string.Join(",",book)); // [A,B,C,D]
4 ChangeReference(out book);
5 Console.WriteLine("[{0}]",string.Join(",",book)); // [W,X,Y,Z]
6 ChangeReference(out null); // compile time error, un-assignable reference
7 ChangeReferenceErr(out book);
8}
9static void ChangeReference(out char[] book){
10 book = new[]{'W','X','Y','Z'};
11}
12static void ChangeReferenceErr(out char[] book){
13 Console.WriteLine("[{0}]",string.Join(",",book)); // compilation error, Cannot use before initialization
14 book = new[]{'W','X','Y','Z'}; // do this before using book
15}
Note:
The passed reference should be an assignable reference, meaning you cannot pass null
as a parameter with out
although you can assign null
value to reference inside callee method.
ref
acts as a constraint so that the array reference should be initialized before passing it to another method.
1static void Main(string[] args){
2 char[] book = new[]{'A','B','C','D'};
3 PrintArrayValues(ref book); // [A,B,C,D]
4 char[] bookNull;
5 PrintArrayValues(ref bookNull); // compile time error, use of unassigned local variable
6 char[] upComingBooks = null;
7 PrintArrayValues(ref upComingBooks); // Runtime error, ArgumentNullException, because books is null
8}
9static void PrintArrayValues(ref char[] book){
10 Console.WriteLine("[{0}]",string.Join(",",book));
11}
Note:
Caller method can initialize the reference as null
which will crash the application if you perform any operation which required a not-null reference
ref
is useful when one method wants to send data and receive modified data; it's two way communication. Whereas out
is one way communication every time a reference should be initialized
ref
and out
are also applicable on value type data
ref
and out
1static void add (ref int a){}
2static void add (out int a){} // compilation error, ref and out are not enough to overload a method
The params
keyword allows us to receive any arbitrary number of parameters as an array.
1static void Main(string[] args){
2 TotalAnyLengthArray(1,2,3); // 6
3 TotalAnyLengthArray(1,2,3,3,4,5,5); // 23
4 TotalAnyLengthArray(); // 0
5}
6static void TotalAnyLengthArray(params int[] ints){
7 long sum = 0;
8 for(int i = 0 ; i < ints.Length ; i++){
9 sum += ints[i];
10 }
11 Console.WriteLine("Total is {0}",sum);
12}
You can pass separate int parameters by keeping params
as the last parameter:
1static void Main(string[] args){
2 TotalAnyLengthArrayAndVerify(6,1,2,3); // True
3 TotalAnyLengthArrayAndVerify(20,1,2,3,3,4,5,5); // False, total is 23 not 20
4}
5static void TotalAnyLengthArrayAndVerify(int expectedTot, params int[] ints){
6 long sum = 0;
7 for(int i = 0 ; i < ints.Length ; i++){
8 sum += ints[i];
9 }
10 Console.WriteLine("Match is {0}", sum == expectedTot);
11}
0
and type of data can be restricted by the type of the array.1object[] all = new object[]{1,true,""}; // object can store any type
2object[] listInt = new int[] {1, 2}; // not possible
Looping an array is almost twice as fast as lists.
==
. Instead, use SequenceEqual
with arrays as:1char[] nName = new[]{'P', 'A', 'V' };
2Console.WriteLine(nName == new char[]{'P','A','V'}); // False
3Console.WriteLine(nName.SequenceEqual(new char[]{'P','A','V'})); // True
null
(to avoid null checks) in case there is an issue with data availability or consistency.1char[] a = new char[]{}; // create empty array
1char[] chars = {'P','A','V','N','E','E','T'};
2string name = new string(chars); //PAVNEET
Share your appreciation and press like on this guide. Thank you for reading!