Lists C# Help

For dynamic lists, the.NET Framework offers the classes ArrayList and List<T>.The class List<T> in the namespace System .:collections.Generic is very similar in its usage to the ArrayList class from the namespace System.Collections.This class implements the IList,ICollection~and IEnumerable interfaces. already discussed the methods of these interfaces,thissectionlooks athow to use the List<T> class.

The following examples use the members of the classRacer as elements tobe added to the collection to represent a Formula-l racer.This classhas four fields:firstNarne, lastName, country, and the number of with s. The fieldscan be accessed with properties.With the constructor of the class,the name of the racer and the number of wins can be passed to setthe members. The method ToString () is overridden to return the name of the racer.The classRacer also implements the generic interfaceIComparer<T> for
sortingracer elements.

Capture1

Capture

Creating Lists

You can create list objects by invoking the default constructor. With the generic class List<T>, you must specify the type for the values of the list with the declaration. The code shows how to declare a List<T> with int and a list with Racer elements. ArrayList is a non-generic list that accepts any Object type for its elements.

Using the default constructor creates an empty list. As soon as elements are added to the list, the capacity of the list is extended to allow four elements. U the fifth element is added, the list is resized to allow eight elements. If eight elements are not enough, the list is resized again to contain 16 elements. With every resize the capacity of the list is doubled.

ArrayList objectList = new ArrayList();
List<int> intList = new List<int>();
List<Racer> racers = new List<Racer>();

U the capacity of the list changes, the complete collection is reallocated to a new memory block. With the implementation of List<T>, an array of type T is used. With reallocation, a new array is created, and Array. Copy () copies the elements from the old to the new array. To save time, if you know the number of elements in advance, that should be in the list; you can define the capacity with the constructor. Here a collection with a capacity of 10 elements is created. If the capacity is not large enough for the elements added, the capacity is resized to 20 and 40 elements – doubled again.

ArrayList objectList = new ArrayList(lO);
List<int> intList = new List<int>(lO);

You can get and set the capacity of a collection by using the Capaci ty property:

objectList.Capacity = 20;
intList.Capacity = 20;

The capacity is not the same as the number of elements in the collection. The number of elements in the collection can be read with the Count property. Of course, the capacity is always larger or equal to the number of items. As long as no element was added to the list, the count is O.

Console.WriteLine(intList.Count);

If you are finished adding elements to the list and don’t want to add any more elements, you can get rid of the unneeded capacity by invoking the TrimExcess () method. However, because the relocation takes time, TrimExcess () does nothing if the item count is more than 90 percent of capacity.

intList.TrimExcess() ;

Because with new applications usually you can use the generic Li s t <T> class instead of the non-generic ArrayList class, and also because the methods of ArrayList are very similar, the reminder of this section focuses just on List<T>.

Collection Inltlallzers

C# 3.0 allows you to assign values to collections using collection initializers. The syntax of collection initializers is similar to array initializers, which were explained . With a collection initializer, values are assigned to the collection within curly brackets at the initialization of the collection:

List <int> intList = new List<int>() (l. 2);
List<string> stringList =
new List<string>() (“one”, “two”);

Collection initializers are a feature of the C# 3.0 programming language and are not reflected within the IL code of the compiled assembly. The compiler converts the collection initializer to invoking the Add ( ) method for every item from the initializer list.

Adding Elements

You can add elements &0 the list with the Add () method as shown. The generic instantiated type defines the parameter type of the Add () method.

List<int> intList new List<int>();
intList.Add(l);
intList.Add(2);
List<string> stringList new List<string>();
stringList.Add(“one”) ;
stringList.Add(“two”);

The variable racers is defined as type List<Racer>. With the new operator, a new object of the same type is created. Because the class List<T> was instantiated with the concrete class Racer, now only Racer objects can be added with the Add () method. In the following sample code, five Formula-l racers are created and added to the collection. The first three are added using the collection initializer, and the last two are added by invoking the Add () method explicitly.

Capture

With the AddRange () method of the List<T> class, you can add multiple elements to the collection at once. The method AddRange () accepts an object of type IEnumeraple<T>, so you can also pass an array as shown:

racers.AddRange(new Racer[] {
new Racer (‘Niki’, ‘Lauda’, ‘Austria’,
25) r new Racer(“Alain’, ‘Prost’, ‘France’,
51)}) ;

The collection initializer can be used only during declaration of the collection. The AddRange ( ) method can be invoked after the collection is initialized.

If you know ‘Some elements of the collection when instantiating the list, you can also pass any object that implements IEnumerable<T> to the constructor of the class. This is very similar to the AddRange ( ) method.

List<Racer> racers = new List<Racer> (new Racer[] ( new Racer(‘Jochen’, ‘Rindt’, ‘Austria’, 6) ,
new Racer(“Ayrton’, ‘Senna”, “Brazil’,
41) });

Inserting Elements

You can insert elements at a specified position with the Insert () method:

racers.Insert(3, new Racer(“Phil”, “Hill”, “USA’, 3) );

The method InsertRange () offers the capability to insert a number of elements, similarly to the AddRange () method shown earlier.

If the index set is larger than the number of elements in the collection, an exception of type Argument Out Of Range Exception is thrown.

Accessing Elements

All classes that implement the IList and IList<T> interface offer an indexer, so you can access the elements by using an indexer and passing the item number. The first item can be accessed with an index value O.By specifying racers [3]. you will access the fourth element of the list:

Racer rl = racers[3];

Getting the number of elements with the Count property, you can do a for loop to iterate through every item in the collection, and use the indexer to access every item:

for (int i = 0; i < racers.Count; i++)
(
” Console.WriteLine(racers[i]);

}

Indexed access to collection classes is available with ArrayList, StringCollection, and List<T>.

Because List<T> implements the interface IEnumerable, you can iterate through the items in the collection using the foreach statement as well:

foreach (Racer r in racers)
(
Console.WriteLine(r) ;

}

How the foreach statement is resolved by the compiler to make use of the IEnumerable and IEnumerator interfaces is explained.

Instead of using the foreach statement, the List<T> class also offers a ForEach () method that is declared with an Action<T> parameter:

public void ForEach(Action<T> action);

The implementation of ForEach () is shown next. ForEach () iterates through every item of the collection and invokes the method that is passed as parameter for every item.

Capture

For passing a method with ForEach, Action<T> is declared as a delegate that defines a method with void return type and parameter T:

public delegate void Action<T>(T obj);

With a list of Racer items, the handler for the ForEach () method must be declared with a Racer object as parameter and a void return type:

public void ActionHandler(Racer obj);

Because one overload of the Console. WriteLine () method accepts Object as parameter, you can pass the address of this method to the ForEach () method, and every racer of the collection is written to the console:

racers.ForEach(Console.WriteLine);

You can also write an anonymous method that accepts a Racer object as parameter. Here, the format A is used with the ToString () method of the IFormattable interface to display all information of
the racer:

racers.ForEach(
delegate(Racer r)
{
Console.WriteLine(“{O:A}”, r);
});

With C# 3.0 you can also use Lambda expressions with methods accepting a delegate parameter. The same iteration that was imple~ented using an anonymous method is defined with a Lambda expression:

racers.ForEach(
r => Console.WriteLine(“{O:A}”, r ));

Anonymous methods and LAmbda expressions are-explained.

Removing Elements

You can remove elements by index or pass the item that should be removed. Here, the fourth element is removed by passing 3 to RemoveAt ( ) :

racers.RemoveAt(3);

You can also directly pass a Racer object to the Remove() method to remove this elFent. Removing by index is faster, because here the collection must be searched for the item to remove. The Remove() method First searches in the collection to get the index of the item with the IndexOf () method, and then uses the index for remove the item. IndexOf () first checks if the item type implements the interface IEquatable. If it does, the Equals () method of this interface is invoked to find ‘the item in the collection that is the same as the one passed to the method. If this intertace !s”not implemented, the Equals () method of the Object class is used to compare the items. The default implementation of the Equals ( ) method in the Object class does a bitwise compare with value types, but compares only references with reference types.

Here, the racer referenced by the variable graham is removed from the collection. The variable graham was created earlier’when the collection was filled. Because the interface IEquatable and the Object. Equals () method are not overridden with the Racer class, you cannot create a new object with the same content as the item that should be removed and pass it to the Remove() method.

if (!’racers .Remove(graham»
Console.WriteLine( “object not found in collection”);

}

The method RemoveRange() removes a number of items from the collection. The first parameter specifies the index where the removal of items should begin; the second parameter specifies the number
of items to be removed.

int index = 3;
int count = 5;
racers.RemoveRange(index, count);

To remove all items with some specific characteristics from the collection, you can use the RemoveAll ( ) method. This method uses the Predicate<T> parameter when searching for elements, which is discussed next. For removing all elements from the collection, use the Clear () method defined with the ICollection<T> interface.

Searchlng

There are different ways to search for elements in the collection. Youcan get the index to the found item, or the item itself. Youcan use methods such as IndexOf ( ), LastIndexOf (), Findlndex ( ), FindLastlndex (), Find ( ), and FindLast ( ). And for just checking if an item exists, the List<T> class offers the Exists () method.

The method IndexOf () requires an object as parameter and returns the index of the item if it is found inside the collection. If the item is not found, -1 is returned. Remember that IndexOf () is using the IEquatable interface for comparing the elements.

int indexl = racers.lndexOf(mario);

With the IndexOf () method, you can also specify that the complete collection should not be searched, but rather specify an index where the search should start and the number of elements that should be iterated for the comparison.

Instead of searching a specific item with the IndexOf () method, you can search for an item that has some specific characteristics that you can define with the Findlndex () method. Findlndex () requires a parameter of type Predicate:

public int Findlndex(Predicate<T> match);

The Predicate<T> type is a delegate that returns a Boolean value and requires type Tas parameter. This delegate can be used similarly to the Action delegate shown earlier with the ForEach () method. If the predicate returns true, there’s a match and the element is found. If it returns false, the element is not found and the search continues.

public de1egate bool Predicate<T>(T obj);

With the List<T> class that is using Racer objects for type T, you can pass the address of a method that returns a bool and defines a parameter of type Racer to the Findlndex () method. Finding the first _ racer of a specific country, you can create the FindCountry class as shown. The-s i nd () method has the signature and return type defined by the Predicate<T> delegate. The Find () method uses the variable country to Search for a country that you can pass with the constructor of the class.

public class FindCountry
{
public “FindCountry (string country)
{
this.country = country;
} ,
private string country;
public bool FindCountryPredicate(Racer racer)
{
if (xacer == null)
throw new ArgumentNullException(‘racer’);
return r.Country == country;
257

}

}

With the Findlndex () method, you can create a new Instance of the FindCountry () class, pass a country string to the constructor, and pass the address of the Find method. After Findlndex ( ) completes successfully, index2 contains the index of the first item where the Country property of the racer is set to Finland.

int index2 = racers.Findlndex(
new FindCountry(“Finland”) .FindCountryPredicate);

Instead of creating a class with a handler method, you can use a Lambda expression here as well. The result is exactly the same as before. Now the Lambda expression defines the implementation to search for an item where the country property is set to Finland.

int index3 = racers. Findlndex (
r => r.Country == “Finland”);

Similarly to the IndexOf () method, with the Findlndex () method, you can also specify the index where the search should start and the count of items that should be iterated through. To do a search for an index beginning from the last element in the collection, you can use the FindLastlndex () method.

The method Findlndex () returns the index of the found item. Instead of getting the index, you can also get directly to the item in the collection. The Find () method requires a parameter of type Predicate<T>, much like the Findlndex () method. The Find () method here is searching for the first racer in the list that has the FirstName property set to Niki. Of course, you can also do a FindLast () to find the last item that fulfills the predicate.

Racer r = racers.Find(
r => r.FirstName == “Niki”);

To get not only one, but all items that fulfill the requirements of a predicate, you can use the FindAll () method. The FindAll () method uses the same Predicate<T> delegate as the Find () and Findlndex () methods. The FindAll () method does not stop when the first item is found but instead iterates ~ugh every item in the collection and returns all items where the predicate returns true.

With the FindAll () method invoked here, all racer items are returned where the property wins is set to more than 20. All racers that won more than 20 races are referenced from the bigWinners list.

List<Racer> bigWinners = racers.FindAll(
r => r.wins > 20);

 Iterating through the variable bigWinners with a foreach statement gives the following result:

foreach (Racer r in bigWinners)
(
Console.WriteLine(“(O:A)”, r);

}

Michael Schumacher, Germany Wins: 91
Niki Lauda, Austria Wins: 25
Alain Prost, France Wins: 51

The result is not sorted, but this is done next.

Sorting

The List<T> class allows sorting its elements by using the Sort () method. Sort () uses the quick sort algorithm where all elements are compared until the complete list is sorted.

You can use several overloads of the Sort () method. The arguments that can be passed are a generic delegate Comparison<T>, the generic interface IComparer<T>, and a range together with the generic interface IComparer<T>:

public void List<T>.Sort();
public void List<T>.Sort(Comparison<T>);
public void List<T>.Sort(IComparer<T>);
public void List<T>.Sort(Int32, Int32, IComparer<T>);

Using the Sort () method without arguments is possible only if the elements in the collection implement the interface IComparable.

The class Racer implements the interface IComparable<T> to sort racers by the last name:

racers.Sort();
racers.ForEach(Console.WriteLine);

If you need to do a sort other than the default supported by the item types, you need to use other techniques, for example passing an object that implements the IComparer<T> interface.

The class Racer Comparer implements the interface IComparer<T> for Racer types. This class allows you to sort either by the first name, last name, country, or number of wins. The kind of sort that should be done is defined with the inner enumeration type CompareType. The CompareType is set with the constructor of the class RacerComparer. The interface IComparer<Racer> defines the method Compare that is required for sorting. In the implementation of this method, the CompareTo () method of the
string and int types is used.

Capture

Capture1

An instance of the RacerComparer class can now be used with the Sort () method. Passing the enumeration RacerComparer .CompareType. Country sorts the collection by the property Country:

racers.Sort(new RacerComparer(
RacerComparer.CompareType.Country));
racers.ForEach(Console.WriteLine);

Another way to do the sort is by using the overloaded Sort () method, which requires a Comparison<T> delegate:

public Void List<T>.sort(Comparison<T>);

Comparison<T> is a delegate to a method that has two parameters of type T and a return type int.lf the parameter values are equal, the method must return o. H the first parameter is less than the second, a value less than zero must be returned: otherwise, a value greater than zero is returned.

public delegate int Comparison<T>(T x. T y);

Now you can pass a Lambda expression to the Sort () method to do a sort by the number of wins. The two parameters are of type Racer, and in the implementation the Wins properties are compared by using the int method CompareTo (). In the implementation, r2 and rl are used in the reverse order, so the number of wins is sorted in descending order. After the method has been invoked, the complete racer list is sorted based on the number of wins of the racer.

racers. Sort (
(rl. r2) => r2.Wins.CompareTo(rl.Wins));

You can also reverse the order of a complete collection by invoking the Reverse () method.

Type Conversion

With the List<T> method ConvertAll (), all types of a collection can be converted to a different type. The ConvertAll () method uses a Converter delegate that is defined like this:

public sealed delegate TOutput Converter<Tlnput. TOutput>(Tlnput from);

The generic types Tlnput and TOutput are used with the conversion. Tlnput is the argument of the delegate method, and TOu tpu t is the return type.

In this example, all Racer types should be converted to Person types. Whereas the Racer type contains a firstName, lastName, country, and the number of wins, the Person type contains just a name. For the conversion, the country of the racer and race wins can be ignored, but the name must be converted:

[Serializable)
public class Person
private string name;
public Person(string name)
this.name = name;
public override string ToString()
return name

}

}

The conversion happens by invoking the racers. ConvertAll<Person> () method. The argument of this method is defined as a Lambda expression with an argument of type Racer and a Person type that is returned. In the implementation of the Lambda expression, a new Person object is created and returned. For the Person object, the FirstName and LastName are passed to the constructor.

List<Person> persons =
racers.ConvertAll<Person>(
r => new Person(r.FirstName + •• +
r.LastName));

The result of the conversion is a list containing the converted Person objects: persons of type List<Person>.

Read-Only Collections

After collections are created they are read/write. Of course, they must be read/write; otherwise; you couldn’t fill them with any values. However, after the collection is filled, you can create a read only collection. The List<T> collection has the method As Read Only () that returns an object of type Read Only Collection<T>. The class Read Only Collection<T> implements the same interfaces as List<T>, but methods and properties that change the collection throw a Not Supported Exception.

Posted on October 29, 2015 in Collections

Share the Story

Back to Top