C-sharp (C#) collections

*Collections
 Another fundamental component of .NET is the Collections library. Collections allow us to store data that can be grouped together because that is similar. Collections also allow us to organize and sort data, as well as to iterate or go through the data, such as in a loop.

When I mentioned that Collections store data, make sure not to confuse this with a database. Although there might be some similarities, a collection is held in memory and is just a data structure component of .NET, whereas a database is usually held on disk and is a standalone application, not just a temporary data structure used during problem execution.

If you have used an Array or a List in .NET, then you have already used the Collections since those are two of the most common collection types available in the framework. There are many different types of Collections with different behaviors and specifications to meet any development need you might have.

Common collections :
 1. List
List is a generic,so it can work with any type of data such as a custom class or an int or strig or a date. Most collections are generic, although there are some that are not.
C# program :
 class ListDemo
    {
        static void Main(string[] args)
        {
            //display "hello world" to console
            Console.WriteLine("hello world");

            //creating list of students
            List students = new List();

            //adding students name in the list
            students.Add("Sanjeev");
            students.Add("Shusil");
            students.Add("Mike");

            //displays number of items in the list
            Console.WriteLine(students.Count);

            //iterate using foreach loop
            foreach(var student in students)
            {
                Console.WriteLine(student);
            }

            //iterate using for loop
            for (int i = 0; i < students.Count; i++)
            {
                //access collection item using index i
                Console.WriteLine(students[i]);
            }
        }
    }

2.Dictionary : Dictionaries for key-value pairs
A dictionary class is a data structure that represents a collection of keys and values. Pairs of data items that have a one-to-one association. A good example of a dictionary is a configuration or settings file. Many systems use dictionaries or dictionary-like collections for this purpose like Configuring for web site.

Just like a list, it's a generic as well, so we need to supply the types for both the key and the value.
 we can have two duplicate keys inside our dictionary.

  class DictionaryDemo
    {
        static void Main(string[] args)
        {

            //create dictionary of key type string, and value also string
            Dictionary config = new Dictionary();

            //adding the key/value
            config.Add("Domain", "www.example.com");
            config.Add("IP", "192.168.0.1");

            //accessing the value by key
            Console.WriteLine(config["Domain"]);

            //declare dictionary inline
            Dictionary config2 = new Dictionary() { { "Domain", "www.example.com" }, { "IP", "192.168.0.1" }, { "IP", "192.168.0.2" } };

            //we can have two duplicate keys inside our dictionary
            Console.WriteLine(config2["IP"]); //it throws error because config2 has two duplicate IP keys

            //iterate using foreach
            foreach (var item in config)
            {
                //accessing key
                Console.WriteLine(item.Key);

                //accessing value
                Console.WriteLine(item.Value);
            }
        }
    }

Note: We saw that, A dictionary has some of the same behavior as a list, but with extra features of key value pairs instead of just indexed values.


3. ArrayList : ArrayList for dynamic-sized collections
An ArrayList is very similar to a List. In fact, it's deprecated, and replaced by the List collection we looked at earlier. It is good to know its limitations compared to the List collection, since there are some important differences.

  - In order to use it, we need to include the Systems.Collections library, and that's because it's not in the System.Collections generic because it's not a generic.Meaning we can't specify what type we want to store in the collection.
  - Boxing : Without even specifying the type, it seems to work. The reason behind this is something called boxing. Boxing is the process of converting your string, or date, or whatever into a regular C# object.

           //need to use namespace : using System.Collections;
            ArrayList teachers = new ArrayList();

            //string item added
            teachers.Add("sanjeev");
            //integer added
            teachers.Add(1);

            foreach (var item in teachers)
            {
                Console.WriteLine(item);
            }

Boxing example:

            int i = 120;

            //regular C# object
            object obj = i;  //we are boxing the integer into a object

            Console.WriteLine((int)obj);
We are boxing the integer into an object, and so now it loses its integer properties, even though the value is still in there.

Now, what if we wish to access it again? We would need to unbox it, and we would do that by casting it back into an int. And as we can see, we have cast obj, or the object, into our integer. This is the concept behind how we can store any type into an ArrayList, which boxes the value in the background.

 -ArrayList item such as length, we won't have access to it because it's boxed in an object. eg. arrayList[0].length not available :D


4. HashTable : Hashtable for key-value pairs with a HashKey
 A hash table is a non-generic collection that was replaced by dictionary. This is similar to how a non-generic array list was replaced by list. And all the same rules of boxing and unboxing apply.
  //need to use namespace : using System.Collections;
            Hashtable hashtbl = new Hashtable();

            //adding key value
            hashtbl.Add(1, "san");
            hashtbl.Add("ip", "123.2.2.1");

            //accesing key with value
            Console.WriteLine((string)hashtbl[1]);

            Console.WriteLine((string)hashtbl["ip"]);

Note:  Adding an item to the hash table is identical to a dictionary, but just as with an array list, accessing the item requires unboxing.

5. Concurrent Dictionary : Concurrent for thread-safe collections
-  A concurrent collection provides thread-safe capabilities, when a collection needs to be used in a multithreaded application.

We will first take a look at how a dictionary fails when used in multithreading, and then how to fix it with a concurrent dictionary.

let's create two separate threads that will run our add item function. And we'll need to include using System.Threading.

 class Program
    {
        static Dictionary students = new Dictionary();
        static void Main(string[] args)
        {
            //instantiate thread, and pass in new ThreadStart to execute our function add item.
            Thread thread1 = new Thread(new ThreadStart(AddItem));

            Thread thread2 = new Thread(new ThreadStart(AddItem));


            //start the thread same time
            thread1.Start();
            thread2.Start();
        }

        static void AddItem()
        {
            students.Add("ID", "STU205");
        }
    }

Output will be error saying Unhandled Exception: System.ArgumentException: An item with the same key has already been added.

So, our AddItem() function specifies a key of one, and when it gets called again by the other thread, it will try to add it again even though it's already been added by the first thread. And as we know all keys must be unique in a dictionary.

Let's go ahead and fix this with a concurrent dictionary.

Add namespace : using System.Collections.Concurrent;

 class Program
    {
        static ConcurrentDictionary students = new ConcurrentDictionary();
        static void Main(string[] args)
        {
            //instantiate thread, and pass in new ThreadStart to execute our function add item.
            Thread thread1 = new Thread(new ThreadStart(AddItem));

            Thread thread2 = new Thread(new ThreadStart(AddItem));


            //start the thread same time
            thread1.Start();
            thread2.Start();
        }

        static void AddItem()
        {
            // students.Add("ID", "STU205");
            students.TryAdd("ID", "STU205");  //Since it's a concurrent dictionary, instead of an add function we get a try add
        }
    }

Note :  Since it's a concurrent dictionary, instead of an add function we get a try add, and that's an intuitive name because it does just try to add it. If it can't because an item with the same key exists then, it will give up and move on without an error or exception.
 Even though two threads tried to add it, program runs without error. This demonstrates that concurrent dictionaries are thread-safe, and a good choice when working in multithreaded operations.


6. BitArray : BitArray for Booleans
A BitArray collection allows us to count and display bits, as well as perform bitwise operations.
Since we are only storing bits, this means it does not need to be a generic to support multiple types. So we will find it in the systems.collections namespace.

BitArray bits = new BitArray(3); //a BitArray needs to know the size that it will be initialized with

            //add items with index
            bits[0] = false;
            bits[1] = true;

            //add item by set method
            bits.Set(2, false);

            //load bitArray from a file or another array

            bool[] boolValues = new bool[] {true, false, true };
            BitArray bit2s = new BitArray(boolValues); //initializing the bitarray by bool array

            //iterate
            foreach (var item in bits)
            {
                Console.WriteLine(item);
            }


7. Tuple : Tuple for multiple objects of different types
A tuple is not a collection in the sense that it allows us to create a list of items with the same type. But, it does allow us to set items of different types in one object. You can think of it almost like a class with different properties of different types.  A tuple is part of the system namespace and so we should be able to declare it by default.  It is a generic, so we can use any type we want.

  // We instantiate the tuple with int, string, bool
            // We also need to pass in the values of the three items in the constructor for it to work.
            Tuple tupExample = new Tuple(1, "sanjeev", true);

            //another way to create tuple
            //Using static create function that the tuple class provides.
            //And in this case, we don't need to specify the type,
            //we just pass in the values and the tuple will be able to determine the type.

            var myTuple = Tuple.Create(1, "sanjeev", true);

            //output item1 which is 1 from 3 items tuple tupExample
            Console.WriteLine(tupExample.Item1);

            //output item2
            Console.WriteLine(tupExample.Item2);

            //output item3
            Console.WriteLine(tupExample.Item3);

when would be a good time to actually use a tuple?
 - Let's imagine we were getting an object from a server or database. Instead of creating a .net class, it might be simpler to just use a tuple in some cases.
 - Another reason might be to return multiple items from a function, since traditionally, we can only return one item.

Note : Although a tuple in not part of the collection namespace, it does, in a sense, behave like one and provides some interesting features that are good to know about.

8. Stack : Stack for last in, first out
Stack is a last in, first out collection.

            Stack stackExample = new Stack();

            //adding an item to the top
            stackExample.Push("sanjeev");
            stackExample.Push("bhusal");

            //pop the top of the stack
            //and also remove it from the stack
            Console.WriteLine(stackExample.Pop()); //output bhusal

            Console.WriteLine(stackExample.Pop()); //output sanjeev

            //display the top item but does not remove it form stack
            Console.WriteLine(stackExample.Peek());

            //iterate
            //display last item : bhusal
            //then only : sanjeev
            //so, it is first in, last out
            foreach (var item in stackExample)
            {
                Console.WriteLine(item);
            }

Note : As we can see the stack collection is similar to a list collection but with a different order and access methods.


9. Queue : Queue for first in, first out
A queue is a first in, first out collection, so, in a sense, it's the opposite of a stack.

A good analogy for a queue is in a lineup. The first person to line up to get a coffee is also the first to get served, while the last person in the lineup is the last to get served.

 Just like a stack, a queue has its own custom method for adding items called Enqueue.

We have a function for queue called Dequeue, which gives us the first item in our queue and removes it as well.

 //initialize int queue
            Queue myQueue = new Queue();

            //Just like a stack,
            //a queue has its own custom method for adding items called Enqueue

            myQueue.Enqueue(1);
            myQueue.Enqueue(2);
            myQueue.Enqueue(3);

            //iterate, display in same order that Enqueued. 1, 2, 3 mainting order
            foreach (var item in myQueue)
            {
                Console.WriteLine(item);
            }

            Console.WriteLine(myQueue.Dequeue()); //output 1, and removes 1
            Console.WriteLine(myQueue.Dequeue()); //output 2, and removes 2

            Console.WriteLine(myQueue.Peek()); //output the first item, but does't remove

Note: We can see that a queue is very similar to a stack, but with a reversed retrieval order.



10.HashSet : HashSets for objects with a HashKey
 A hash set is an optimized set collection. It helps eliminate duplicate strings or elements in an array.

  //initialise hashset
            var myHash = new HashSet();

            //new array
            string[] s = new string[] { "san" };

            //adding same items twice
            myHash.Add("san");
            myHash.Add("san");

            Console.WriteLine(myHash.Overlaps(s)); //output true

            //output count of hashset
            Console.WriteLine(myHash.Count); //output only 1.

Even though we tried to add two items, there's only one in the hash set. That's because the hash set optimizes the sets and removes duplicates. This could be useful if you receive some data from the network in an array that might contain duplicates. We could then pass this into the constructor of the hash set. And it could take care of optimizing the array for us.

 Next, another useful feature of the hash set collection is an overlap. Overlaps allow us to compare another array or set to see if it contains the same items that the hash set contains.

Comments