# C# Study - Collections
###### tags: `C#` 'Collections`
# Reference
---
# [``IList<T>`` Interface](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.ilist-1?view=netcore-3.1)
Namespace: ``System.Collections.Generic``
Represents a collection of objects that can be individually accessed by index.
```
public interface IList<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>
```
---
# [C# - List<T>](https://www.tutorialsteacher.com/csharp/csharp-list)
The ``List<T>`` is a collection of strongly typed objects
that can be accessed by index and having methods
for sorting, searching, and modifying list.
It is the generic version of the ``ArrayList`` that
comes under ``System.Collection.Generic`` namespace.
## ``List<T>`` Characteristics
* ``List<T>`` equivalent of the ``ArrayList``, which implements ``IList<T>``.
* It comes under System.Collection.Generic namespace.
* ``List<T>`` can contain elements of the specified type.
It __provides compile-time type checking and
doesn't perform boxing-unboxing because it is generic__.
* Elements can be added using the ``Add()``, ``AddRange()`` methods
or collection-initializer syntax.
* Elements can be accessed by passing an index
e.g. ``myList[0]``. __Indexes start from zero__.
* ``List<T>`` performs faster and less error-prone than the ArrayList.
boxing / unboxing
## Creating a List
The ``List<T>`` is a generic collection,
so you __need to specify a type parameter__
for the type of data it can store.
The following example shows how to create list and add elements.
### Example 1 - Adding elements in List:
```
List<int> primeNumbers = new List<int>();
primeNumbers.Add(1); // adding elements using add() method
primeNumbers.Add(3);
primeNumbers.Add(5);
primeNumbers.Add(7);
var cities = new List<string>();
cities.Add("New York");
cities.Add("London");
cities.Add("Mumbai");
cities.Add("Chicago");
cities.Add(null);// nulls are allowed for reference type list
//adding elements using collection-initializer syntax
var bigCities = new List<string>()
{
"New York",
"London",
"Mumbai",
"Chicago"
};
```
In the above example,
``List<int> primeNumbers = new List<int>();``
creates a list of int type. In the same way,
cities and bigCities are string type list.
You can then add elements in a list using
the ``Add()`` method or the __collection-initializer syntax__.
You can also add elements of the custom classes
using the collection-initializer syntax.
The following adds objects of the ``Student`` class
in the ``List<Student>``.
### Example 2 - Add Custom Class Objects in List:
```
var students = new List<Student>() {
new Student(){ Id = 1, Name="Bill"},
new Student(){ Id = 2, Name="Steve"},
new Student(){ Id = 3, Name="Ram"},
new Student(){ Id = 4, Name="Abdul"}
};
```
## Adding an Array in a List
Use the ``AddRange()`` method to add all the elements
from an array or another collection to ``List``.
``AddRange()`` signature:
```
void AddRange(IEnumerable<T> collection)
```
### Example 3 - Add Arrays in List:
```
string[] cities = new string[3]{ "Mumbai", "London", "New York" };
var popularCities = new List<string>();
// adding an array in a List
popularCities.AddRange(cities);
var favouriteCities = new List<string>();
// adding a List
favouriteCities.AddRange(popularCities);
```
## Accessing a List
A list can be accessed by an index,
a for/foreach loop, and using LINQ queries.
Indexes of a list start from zero.
Pass an index in the square brackets
to access individual list items, same as array.
Use a foreach or for loop to iterate a ``List<T>`` collection.
### Example 4 - Accessing List:
```
List<int> numbers = new List<int>() { 1, 2, 5, 7, 8, 10 };
Console.WriteLine(numbers[0]); // prints 1
Console.WriteLine(numbers[1]); // prints 2
Console.WriteLine(numbers[2]); // prints 5
Console.WriteLine(numbers[3]); // prints 7
// using foreach LINQ method
numbers.ForEach(num => Console.WriteLine(num + ", "));//prints 1, 2, 5, 7, 8, 10,
// using for loop
for(int i = 0; i < numbers.Count; i++) {
Console.WriteLine(numbers[i]);
}
```
## Accessing a List using LINQ
The ``List<T>`` implements the ``IEnumerable`` interface.
So, we can query a list using __LINQ query syntax or method syntax__, as shown below.
### Example 5 - LINQ Query on List:
```
var students = new List<Student>() {
new Student(){ Id = 1, Name="Bill"},
new Student(){ Id = 2, Name="Steve"},
new Student(){ Id = 3, Name="Ram"},
new Student(){ Id = 4, Name="Abdul"}
};
//get all students whose name is Bill
var result = from s in students
where s.Name == "Bill"
select s;
foreach(var student in result) {
Console.WriteLine(student.Id + ", " + student.Name);
}
```
## Insert Elements in List
Use the Insert() method inserts an element into the List<T> collection at the specified index.
Insert() signature:void Insert(int index, T item);
### Example 6 - Insert elements into List:
```
var numbers = new List<int>(){ 10, 20, 30, 40 };
numbers.Insert(1, 11);// inserts 11 at 1st index: after 10.
foreach(var num in numbers) {
Console.Write(num);
}
```
## Remove Elements from List
Use the ``Remove()`` method to remove
the first occurrence of the specified element in the ``List<T>`` collection.
Use the ``RemoveAt()`` method to remove an element from the specified index.
If no element at the specified index,
then the ``ArgumentOutOfRangeException`` will be thrown.
``Remove()`` signature: ``bool Remove(T item)``
``RemoveAt()`` signature: ``void RemoveAt(int index)``
### Example 7 - Remove elements from List:
```
var numbers = new List<int>(){ 10, 20, 30, 40, 10 };
numbers.Remove(10); // removes the first 10 from a list
numbers.RemoveAt(2); //removes the 3rd element (index starts from 0)
//numbers.RemoveAt(10); //throws ArgumentOutOfRangeException
foreach(var el in intList) {
Console.Write(el); //prints 20 30
}
```
## Check Elements in List
Use the ``Contains()`` method to determine whether an element is in the List<T> or not.
### Example 8 - Contains():
```
var numbers = new List<int>(){ 10, 20, 30, 40 };
numbers.Contains(10); // returns true
numbers.Contains(11); // returns false
numbers.Contains(20); // returns true
```
## ``List<T>`` Class Properties and Methods
The following table lists the important properties and methods of ``List<T>`` class:
* ``Items``:
Gets or sets the element at the specified index
* ``Count``:
Returns the total number of elements exists in the ``List<T>``
---
# [What is the difference between List and IList in C#?](https://www.tutorialspoint.com/what-is-the-difference-between-list-and-ilist-in-chash)
The main difference between ``List`` and ``IList`` in C# is that
__``List`` is a class__ that represents __a list of objects——
which can be accessed by index while __``IList`` is an interface__
that represents a __collection of objects__
which can be accessed by index. The ``IList`` interface implemented
from two interfaces and they are ``ICollection`` and ``IEnumerable``.
``List`` and ``IList`` are used to denote a set of objects.
They can store objects of integers, strings, etc.
There are methods to insert, remove elements,
search and sort elements of a ``List`` or ``IList``.
The major difference between ``List`` and ``IList`` is that
__``List`` is a concrete class and ``IList`` is an interface__.
Overall, ``List`` is a concrete type that __implements the ``IList`` interface__.
### Example 1:
```
using System;
using System.Collections.Generic;
namespace DemoApplication{
class Demo{
static void Main(string[] args) {
IList<string> ilist = new IList<string>();
//This will throw error as we cannot create instance for an IList as it is an interface.
ilist.Add("Mark");
ilist.Add("John");
foreach(string list in ilist) {
Console.WriteLine(list);
}
}
}
}
```
### Example 2:
```
using System;
using System.Collections.Generic;
namespace DemoApplication{
class Demo{
static void Main(string[] args) {
IList<string> ilist = new List<string>();
ilist.Add("Mark");
ilist.Add("John");
List<string> list = new List<string>();
ilist.Add("Mark");
ilist.Add("John");
foreach(string lst in ilist) {
Console.WriteLine(lst);
}
foreach(string lst in list) {
Console.WriteLine(lst);
}
Console.ReadLine();
}
}
}
```
---
# [``IDictionary<TKey,TValue>`` Interface](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.idictionary-2?view=netcore-3.1)
Namespace: ``System.Collections.Generic``
Represents a generic collection of key/value pairs.
```
public interface IDictionary<TKey,TValue> : System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey,TValue>>, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey,TValue>>
```
## Examples
The following code example creates
an empty ``Dictionary<TKey,TValue>`` of strings,
with ``string`` keys, and accesses it
through the ``IDictionary<TKey,TValue>`` interface.
The code example uses the ``Add`` method to add some elements.
The example demonstrates that the ``Add`` method
__throws ``ArgumentException`` when attempting to add a duplicate key__.
The example uses the __``Item[]`` property__ (the indexer in C#)
to retrieve values, demonstrating that
__a ``KeyNotFoundException`` is thrown when a requested key is not present__,
and showing that __the value associated with a key can be replaced__.
加值時,要先測試 key 存不存在;
另外,取值時,
``Item[]`` property - indexer 可以取值或更新值,但 key 不存在會?
The example shows __how to use the ``TryGetValue`` method
as a more efficient way to retrieve values
if a program often must try key values that are not in the dictionary__,
and __how to use the ``ContainsKey`` method to test
whether a ``key`` exists prior to calling the ``Add`` method__.
Finally, the example shows
__how to enumerate the keys and values__ in the dictionary, and
__how to enumerate the values alone using the ``Values`` property__.
```
using System;
using System.Collections.Generic;
public class Example {
public static void Main() {
// Create a new dictionary of strings, with string keys,
// and access it through the IDictionary generic interface.
IDictionary<string, string> openWith = new Dictionary<string, string>();
// Add some elements to the dictionary.
// There are no duplicate keys, but some of the values are duplicates.
openWith.Add("txt", "notepad.exe");
openWith.Add("bmp", "paint.exe");
openWith.Add("dib", "paint.exe");
openWith.Add("rtf", "wordpad.exe");
// The Add method throws an exception
// if the new key is already in the dictionary.
try {
openWith.Add("txt", "winword.exe");
}
catch (ArgumentException) {
Console.WriteLine("An element with Key = \"txt\" already exists.");
}
// The Item property is another name for the indexer,
// so you can omit its name when accessing elements.
Console.WriteLine("For key = \"rtf\", value = {0}.", openWith["rtf"]);
// The indexer can be used to change the value associated with a key.
openWith["rtf"] = "winword.exe";
Console.WriteLine("For key = \"rtf\", value = {0}.", openWith["rtf"]);
// If a key does not exist,
// setting the indexer for that key adds a new key/value pair.
openWith["doc"] = "winword.exe";
// The indexer throws an exception if the requested key is not in the dictionary.
try {
Console.WriteLine("For key = \"tif\", value = {0}.", openWith["tif"]);
}
catch (KeyNotFoundException) {
Console.WriteLine("Key = \"tif\" is not found.");
}
// When a program often has to try keys that
// turn out not to be in the dictionary,
// TryGetValue can be a more efficient way to retrieve values.
string value = "";
if (openWith.TryGetValue("tif", out value)) {
Console.WriteLine("For key = \"tif\", value = {0}.", value);
}
else {
Console.WriteLine("Key = \"tif\" is not found.");
}
// ContainsKey can be used to test keys before inserting them.
if (!openWith.ContainsKey("ht")) {
openWith.Add("ht", "hypertrm.exe");
Console.WriteLine("Value added for key = \"ht\": {0}",
openWith["ht"]);
}
// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach(KeyValuePair<string, string> kvp in openWith) {
Console.WriteLine("Key = {0}, Value = {1}",
kvp.Key, kvp.Value);
}
// To get the values alone, use the Values property.
ICollection<string> icoll = openWith.Values;
// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach(string s in icoll) {
Console.WriteLine("Value = {0}", s);
}
// To get the keys alone, use the Keys property.
icoll = openWith.Keys;
// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach(string s in icoll) {
Console.WriteLine("Key = {0}", s);
}
// Use the Remove method to remove a key/value pair.
Console.WriteLine("\nRemove(\"doc\")");
openWith.Remove("doc");
if (!openWith.ContainsKey("doc")) {
Console.WriteLine("Key \"doc\" is not found.");
}
}
}
```
This code example produces the following output:
```
An element with Key = "txt" already exists.
For key = "rtf", value = wordpad.exe.
For key = "rtf", value = winword.exe.
Key = "tif" is not found.
Key = "tif" is not found.
Value added for key = "ht": hypertrm.exe
Key = txt, Value = notepad.exe
Key = bmp, Value = paint.exe
Key = dib, Value = paint.exe
Key = rtf, Value = winword.exe
Key = doc, Value = winword.exe
Key = ht, Value = hypertrm.exe
Value = notepad.exe
Value = paint.exe
Value = paint.exe
Value = winword.exe
Value = winword.exe
Value = hypertrm.exe
Key = txt
Key = bmp
Key = dib
Key = rtf
Key = doc
Key = ht
Remove("doc")
Key "doc" is not found.
```
## Remarks
The ``IDictionary<TKey,TValue>`` interface is the base interface
for generic collections of key/value pairs.
__Each element is a key/value pair stored in a ``KeyValuePair<TKey,TValue>`` object__.
__Each pair must have a unique key__.
Implementations can vary in __whether they allow key to be ``null``__.
__The value can be ``null`` and does not have to be unique__.
The ``IDictionary<TKey,TValue>`` interface allows
the contained keys and values to be enumerated,
but it __does not imply any particular sort order__.
The ``foreach`` statement of the C# language
(For Each in Visual Basic, for each in C++)
returns an object of the type of the elements in the collection.
Since each element of the ``IDictionary<TKey,TValue>``
is a __key/value pair__, the element type is not
the type of the key or the type of the value. Instead,
__the element type is KeyValuePair<TKey,TValue>__.
For example:
```
foreach (KeyValuePair<int, string> kvp in myDictionary) {
Console.WriteLine("Key = {0}, Value = {1}", kvp.Key, kvp.Value);
}
```
The ``foreach`` statement is a __wrapper around the enumerator__,
which __only allows reading from, not writing to__, the collection.
只能讀,不能寫!!
---
[C# - Dictionary<TKey, TValue>](https://www.tutorialsteacher.com/csharp/csharp-dictionary)
The ``Dictionary<TKey, TValue>`` is a generic collection
that stores key-value pairs __in no particular order__.
## Dictionary Characteristics
* ``Dictionary<TKey, TValue>`` stores key-value pairs.
* Comes under ``System.Collection.Generic`` namespace.
* __Implements ``IDictionary<TKey, TValue>`` interface__.
* __Keys must be unique and cannot be ``null``__.
* __Values can be null or duplicate__.
* Values can be accessed by passing associated key
in the indexer e.g. ``myDictionary[key]``
* Elements are stored as ``KeyValuePair<TKey, TValue>`` objects.
## Creating a Dictionary
You can create the ``Dictionary<TKey, TValue>`` object
by passing the type of keys and values it can store.
The following example shows how to create a dictionary
and add key-value pairs.
### Example 1 - Create Dictionary and Add Elements:
```
IDictionary<int, string> numberNames = new Dictionary<int, string>();
numberNames.Add(1,"One"); //adding a key/value using the Add() method
numberNames.Add(2,"Two");
numberNames.Add(3,"Three");
//The following throws run-time exception: key already added.
//numberNames.Add(3, "Three");
foreach(KeyValuePair<int, string> kvp in numberNames) {
Console.WriteLine("Key: {0}, Value: {1}", kvp.Key, kvp.Value);
}
//creating a dictionary using collection-initializer syntax
var cities = new Dictionary<string, string>(){
{"UK", "London, Manchester, Birmingham"},
{"USA", "Chicago, New York, Washington"},
{"India", "Mumbai, New Delhi, Pune"}
};
foreach(var kvp in cities) {
Console.WriteLine("Key: {0}, Value: {1}", kvp.Key, kvp.Value);
}
```
In the above example, numberNames is a Dictionary<int, string> type dictionary, so it can store int keys and string values. In the same way, cities is a Dictionary<string, string> type dictionary, so it can store string keys and string values. Dictionary cannot include duplicate or null keys, whereas values can be duplicated or null. Keys must be unique otherwise, it will throw a runtime exception.
## Access Dictionary Elements
The Dictionary can be accessed using __indexer__.
Specify a key to get the associated value.
You can also use the ``ElementAt()`` method to
__get a ``KeyValuePair`` from the specified index__.
### Example 2 - Access Dictionary Elements:
```
var cities = new Dictionary<string, string>(){
{"UK", "London, Manchester, Birmingham"},
{"USA", "Chicago, New York, Washington"},
{"India", "Mumbai, New Delhi, Pune"}
};
Console.WriteLine(cities["UK"]); //prints value of UK key
Console.WriteLine(cities["USA"]);//prints value of USA key
//Console.WriteLine(cities["France"]); // run-time exception: Key does not exist
//use ContainsKey() to check for an unknown key
if(cities.ContainsKey("France")){
Console.WriteLine(cities["France"]);
}
//use TryGetValue() to get a value of unknown key
string result;
if(cities.TryGetValue("France", out result)) {
Console.WriteLine(result);
}
//use ElementAt() to retrieve key-value pair using index
for(int i = 0; i < cities.Count; i++) {
Console.WriteLine("Key: {0}, Value: {1}", cities.ElementAt(i).Key, cities.ElementAt(i).Value);
}
```
## Update Dictionary
Update the value of a key by specifying a key in the indexer. It will throw the KeyNotFoundException if a key does not exist in the dictionary, therefore use the ContainsKey() method before accessing unknown keys.
### Example 3 - Update Dictionary Elements:
```
var cities = new Dictionary<string, string>(){
{"UK", "London, Manchester, Birmingham"},
{"USA", "Chicago, New York, Washington"},
{"India", "Mumbai, New Delhi, Pune"}
};
cities["UK"] = "Liverpool, Bristol"; // update value of UK key
cities["USA"] = "Los Angeles, Boston"; // update value of USA key
//cities["France"] = "Paris"; //throws run-time exception: KeyNotFoundException
if(cities.ContainsKey("France")) {
cities["France"] = "Paris";
}
```
## Remove Elements in Dictionary
The ``Remove()`` method
__deletes an existing key-value pair__ from a dictionary.
The ``Clear()`` method __deletes all the elements__ of the dictionary.
### Example 4 - Remove Dictionary Elements:
```
var cities = new Dictionary<string, string>(){
{"UK", "London, Manchester, Birmingham"},
{"USA", "Chicago, New York, Washington"},
{"India", "Mumbai, New Delhi, Pune"}
};
cities.Remove("UK"); // removes UK
//cities.Remove("France"); //throws run-time exception: KeyNotFoundException
if(cities.ContainsKey("France")) { // check key before removing it
cities.Remove("France");
}
cities.Clear(); //removes all elements
```
---
## Next: Sort a List
* [Comparisons and sorts within collections](https://docs.microsoft.com/en-us/dotnet/standard/collections/comparisons-and-sorts-within-collections)
* [List<T>.Sort Method](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.sort?view=netcore-3.1)
* [Comparison<T> Delegate](https://docs.microsoft.com/en-us/dotnet/api/system.comparison-1?view=netcore-3.1)
* [IComparer<T> Interface](https://docs.microsoft.com/en-us/dotnet/api/system.collections.generic.icomparer-1?view=netcore-3.1)
---
* [How to sort a list in C# | List.Sort() Method Set -1](https://www.geeksforgeeks.org/how-to-sort-list-in-c-sharp-set-1/)
* [How to sort a list in C# | List.Sort() Method Set -2](https://www.geeksforgeeks.org/how-to-sort-a-list-in-c-sharp-list-sort-method-set-2/?ref=lbp)
---
# [Using indexers (C# Programming Guide)](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/using-indexers)
__Indexers__ are a __syntactic convenience__ that enable you
to create a ``class``, ``struct``, or interface that
client applications can access as an array.
The compiler will generate an ``Item`` property
(or an alternatively named property if ``IndexerNameAttribute`` is present),
and the appropriate accessor methods.
Indexers are most frequently implemented in types
whose __primary purpose is to encapsulate an internal collection or array__.
For example, suppose you have a class ``TempRecord`` that
represents the temperature in ``Fahrenheit`` as recorded
at 10 different times during a 24-hour period.
The class contains a temps array of type ``float[]``
to store the temperature values.
By implementing an indexer in this class,
clients can access the temperatures in a ``TempRecord`` instance
as ``float temp = tempRecord[4]`` instead of
as ``float temp = tempRecord.temps[4]``.
The indexer notation not only simplifies the syntax for client applications;
it also makes the class, and its purpose more intuitive for other developers to understand.
To declare an indexer on a class or struct,
use the ``this`` keyword, as the following example shows:
```
// Indexer declaration
public int this[int index] {
// get and set accessors
}
```
__Important__
Declaring an indexer will automatically generate a property named ``Item`` on the object.
The ``Item`` property is not directly accessible from the instance __member access expression__.
Additionally, if you add your own ``Item`` property to an object with an indexer,
you'll get a CS0102 compiler error. To avoid this error, use the IndexerNameAttribute rename the indexer as detailed below.
---
## Remarks
__The type of an indexer and the type of its parameters
must be at least as accessible as the indexer itself__.
For more information about accessibility levels, see [Access Modifiers](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/indexers-in-interfaces).
For more information about how to use indexers with an interface, see [Interface Indexers](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/indexers-in-interfaces).
The signature of an indexer consists of the number and
types of its formal parameters. It doesn't include
the indexer type or the names of the formal parameters.
If you declare more than one indexer in the same class,
they must have different signatures.
An indexer value is not classified as a variable; therefore,
you cannot pass an indexer value as a ``ref`` or ``out`` parameter.
To provide the indexer with a name that other languages can use,
use [``System.Runtime.CompilerServices.IndexerNameAttribute``](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.indexernameattribute),
as the following example shows:
```
// Indexer declaration
[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this[int index] {
// get and set accessors
}
```
This indexer will have the name ``TheItem``,
as it is overridden by the indexer name attribute.
By default, the indexer name is ``Item``.
### Example:
The following example shows how to declare a ``private`` array field,
``temps``, and an indexer. The indexer enables direct access
to the instance ``tempRecord[i]``.
__The alternative to using the indexer__ is to
__declare the array as a ``public`` member and access its members__,
``tempRecord.temps[i]``, directly.
---
```
public class TempRecord {
// Array of temperature values
float[] temps = new float[10] {
56.2F, 56.7F, 56.5F, 56.9F, 58.8F,
61.3F, 65.9F, 62.1F, 59.2F, 57.5F
};
// To enable client code to validate input
// when accessing your indexer.
public int Length => temps.Length;
// Indexer declaration.
// If index is out of range, the temps array will throw the exception.
public float this[int index] {
get => temps[index];
set => temps[index] = value;
}
}
```
```
static void Main() {
var tempRecord = new TempRecord();
// Use the indexer's set accessor
tempRecord[3] = 58.3F;
tempRecord[5] = 60.1F;
// Use the indexer's get accessor
for (int i = 0; i < 10; i++) {
Console.WriteLine($"Element #{i} = {tempRecord[i]}");
}
}
```
---