The
classic problem with the Array
type is its fixed
size. If you do not know in advance how many objects an array will
hold, you run the risk of declaring either too small an array (and
running out of room) or too large an array (and wasting memory).
Your program might be asking the user for input, or gathering input from a web site. As it finds objects (strings, books, values, etc.) you will add them to the array, but you have no idea how many objects you’ll collect in any given session. The classic fixed-size array is not a good choice, as you can’t predict how large an array you’ll need.
The ArrayList
class is an array whose size is
dynamically increased as required. ArrayLists
provide a number of useful methods and properties for their
manipulation. Some of the most important are shown in Table 9-3.
Table 9-3. ArrayList methods and properties
When you create an ArrayList
, you do not define
how many objects it will contain. You add to the
ArrayList
using the Add( )
method, and the list takes care of its own internal bookkeeping, as
illustrated in Example 9-12.
Example 9-12. Working with an ArrayList
namespace Programming_CSharp { using System; using System.Collections; // a simple class to store in the array public class Employee { public Employee(int empID) { this.empID = empID; } public override string ToString( ) { return empID.ToString( ); } public int EmpID { get { return empID; } set { empID = value; } } private int empID; } public class Tester { static void Main( ) {ArrayList empArray = new ArrayList( );
ArrayList intArray = new ArrayList( );
// populate the array
for (int i = 0;i<5;i++)
{
empArray.Add(new Employee(i+100));
intArray.Add(i*5);
}
// print all the contents for (int i = 0;i<intArray.Count;i++) { Console.Write("{0} ", intArray[i].ToString( )); } Console.WriteLine(" "); // print all the contents of the button array for (int i = 0;i<empArray.Count;i++) { Console.Write("{0} ", empArray[i].ToString( )); } Console.WriteLine(" "); Console.WriteLine("empArray.Capacity: {0}", empArray.Capacity); } } } Output: 0 5 10 15 20 100 101 102 103 104 empArray.Capacity: 16
With an Array
class, you define how many objects
the array will hold. If you try to add more than that, the
Array
class will throw an exception. With an
ArrayList
you do not declare how many objects the
ArrayList
will hold. The
ArrayList
has a property,
Capacity
, which is the number of elements the
ArrayList
is capable of storing:
public int Capcity {virtual get; virtual set; }
The default capacity is 16. When you add the 17th element, the
capacity is automatically doubled to 32. If you change the
for
loop to:
for (int i = 0;i<17;i++)
the output looks like this:
0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 MyButtonArray.Capacity: 32
You can manually set the capacity to any number equal to or greater
than the count. If you set it to a number less than the count, the
program will throw an exception of type
ArgumentOutOfRangeException
.
Like all
collections, the ArrayList
implements the
Sort( )
method, which allows you to sort
any objects that implement IComparable
. In the
next example, you’ll modify the Employee
object to implement IComparable
:
public class Employee : IComparable
To implement the IComparable
interface, the
Employee
object must provide a
CompareTo()
method:
public int CompareTo(Object rhs) { Employee r = (Employee) rhs; return this.empID.CompareTo(r.empID); }
The
CompareTo( )
method takes an object as a parameter; the
Employee
object must compare itself to this object
and return -1
if it is smaller than the object,
1
if it is greater than the object, and
0
if it is equal to the object. It is up to
Employee
to determine what smaller than
, greater than
,and equal to
mean. For example, you will
cast the object to an Employee
and then delegate
the comparison to the empId
member, which is an
int
and which will use the default
CompareTo( )
method for integer types, which will
do an integer comparison of the two values.
Because int
derives from object
it has methods, including the method CompareTo()
.
Thus int
is an object
to which
you may delegate the responsibility of comparison.
You are now ready to sort the array list of employees,
empList
. To see if the sort is working,
you’ll need to add integers and Employee
instances to their respective arrays with random values. To create
the random values, you’ll instantiate an object of class
Random
, and to generate the random values
you’ll call the
Next( )
method on the
Random
object, which returns a pseudo-random
number. The Next( )
method is overloaded; one
version allows you to pass in an integer which represents the largest
random number you want. In this case, you’ll pass in the value
10
to generate a random number between
0
and 10
:
Random r = new Random( ); r.Next(10);
Example 9-13 creates an integer array and an
Employee
array, populates them both with random
numbers, and prints their values. It then sorts both arrays and
prints the new values.
Example 9-13. Sorting an integer and an employee array
namespace Programming_CSharp { using System; using System.Collections; // a simple class to store in the array public class Employee : IComparable { public Employee(int empID) { this.empID = empID; } public override string ToString( ) { return empID.ToString( ); } // Comparer delegates back to Employee // Employee uses the integer's default // CompareTo methodpublic int CompareTo(Object rhs)
{
Employee r = (Employee) rhs;
return this.empID.CompareTo(r.empID);
}
private int empID; } public class Tester { static void Main( ) { ArrayList empArray = new ArrayList( ); ArrayList intArray = new ArrayList( ); // generate random numbers for // both the integers and the // employee id'sRandom r = new Random( );
// populate the array
for (int i = 0;i<5;i++)
{
// add a random employee id
empArray.Add(new Employee(r.Next(10)+100));
// add a random integer
intArray.Add(r.Next(10));
}
// display all the contents of the int array for (int i = 0;i<intArray.Count;i++) { Console.Write("{0} ", intArray[i].ToString( )); } Console.WriteLine(" "); // display all the contents of the Employee array for (int i = 0;i<empArray.Count;i++) { Console.Write("{0} ", empArray[i].ToString( )); } Console.WriteLine(" "); // sort and display the int array intArray.Sort( ); for (int i = 0;i<intArray.Count;i++) { Console.Write("{0} ", intArray[i].ToString( )); } Console.WriteLine(" "); // sort and display the employee array //Employee.EmployeeComparer c = Employee.GetComparer( ); //empArray.Sort(c);empArray.Sort( );
// display all the contents of the Employee array for (int i = 0;i<empArray.Count;i++) { Console.Write("{0} ", empArray[i].ToString( )); } Console.WriteLine(" "); } } } Output: 8 5 7 3 3 105 103 102 104 106 3 3 5 7 8 102 103 104 105 106
The output shows that the integer array and
Employee
array were generated with random numbers,
and then when sorted the display shows the values have been ordered
properly.
When you
call Sort( )
on the
ArrayList
the default implementation of
IComparer
is called, which uses
QuickSort
to call the
IComparable
implementation of CompareTo( )
on each element in the
ArrayList
.
You are free to create your own implementation of
IComparer
, which you might want to do if you need
control over how the sort is accomplished. For example, in the next
example, you will add a second field to Employee
,
yearsOfSvc
. You want to be able to sort the
Employee
objects in the
ArrayList
on either field,
empID
or yearsOfSvc
.
To accomplish this, you will create a custom implementation of
IComparer
which you will pass to the
Sort( )
method of ArrayList
.
This IComparer
class,
EmployeeComparer
, knows about
Employee
objects and knows how to sort them.
EmployeeComparer
has a property,
WhichComparison
, of type
Employee.EmployeeComparer.ComparisonType
:
public Employee.EmployeeComparer.ComparisonType WhichComparison { get { return whichComparison; } set { whichComparison=value; } }
ComparisonType
is an enumeration with two values,
empID
or yearsOfSvc
(indicating
that you want to sort by employee ID or years of service,
respectively):
public enum ComparisonType { EmpID, Yrs };
Before invoking Sort( )
, you will create an
instance of EmployeeComparer
and set its
ComparisionType
property:
Employee.EmployeeComparer c = Employee.GetComparer( ); c.WhichComparison=Employee.EmployeeComparer.ComparisonType.EmpID; empArray.Sort(c);
When you invoke Sort( )
the ArrayList will call
the Compare
method on the
EmployeeComparer
, which in turn will delegate the
comparison to the Employee.CompareTo( )
method,
passing in its property WhichComparison.public int
:
Compare(object lhs, object rhs) { Employee l = (Employee) lhs; Employee r = (Employee) rhs; return l.CompareTo(r,WhichComparison); }
The Employee
object must implement a custom
version of CompareTo( )
which takes the comparison
and compares the objects accordingly:
public int CompareTo( Employee rhs, Employee.EmployeeComparer.ComparisonType which) { switch (which) { case Employee.EmployeeComparer.ComparisonType.EmpID: return this.empID.CompareTo(rhs.empID); case Employee.EmployeeComparer.ComparisonType.Yrs: return this.yearsOfSvc.CompareTo(rhs.yearsOfSvc); } return 0; }
The complete source for this example is shown in Example 9-14. The integer array has been removed to
simplify the example, and the output of the employee’s
ToString( )
method enhanced to enable you to see
the effects of the sort.
Example 9-14. Sorting an array by employees’ IDs and years of service
namespace Programming_CSharp { using System; using System.Collections; // a simple class to store in the array public class Employee : IComparable { public Employee(int empID) { this.empID = empID; }public Employee(int empID, int yearsOfSvc)
{
this.empID = empID;
this.yearsOfSvc = yearsOfSvc;
}
public override string ToString( )
{
return "ID: " + empID.ToString( ) +
". Years of Svc: " + yearsOfSvc.ToString( );
}
// static method to get a Comparer object
public static EmployeeComparer GetComparer( )
{
return new Employee.EmployeeComparer( );
}
// Comparer delegates back to Employee // Employee uses the integer's default // CompareTo method public int CompareTo(Object rhs) { Employee r = (Employee) rhs; return this.empID.CompareTo(r.empID); }// Special implementation to be called by custom comparer
public int CompareTo(
Employee rhs,
Employee.EmployeeComparer.ComparisonType which)
{
switch (which)
{
case Employee.EmployeeComparer.ComparisonType.EmpID:
return this.empID.CompareTo(rhs.empID);
case Employee.EmployeeComparer.ComparisonType.Yrs:
return this.yearsOfSvc.CompareTo(rhs.yearsOfSvc);
}
return 0;
}
// nested class which implements IComparer
public class EmployeeComparer : IComparer
{
// enumeration of comparsion types
public enum ComparisonType
{
EmpID,
Yrs
};
// Tell the Employee objects to compare themselves
public int Compare(object lhs, object rhs)
{
Employee l = (Employee) lhs;
Employee r = (Employee) rhs;
return l.CompareTo(r,WhichComparison);
}
public Employee.EmployeeComparer.ComparisonType
WhichComparison
{
get
{
return whichComparison;
}
set
{
whichComparison=value;
}
}
// private state variable
private Employee.EmployeeComparer.ComparisonType
whichComparison;
}
private int empID;private int yearsOfSvc = 1;
} public class Tester { static void Main( ) { ArrayList empArray = new ArrayList( ); // generate random numbers for // both the integers and the // employee id's Random r = new Random( ); // populate the array for (int i = 0;i<5;i++) { // add a random employee idempArray.Add(
new Employee(
r.Next(10)+100,r.Next(20)
) ); } // display all the contents of the Employee array for (int i = 0;i<empArray.Count;i++) { Console.Write(" {0} ", empArray[i].ToString( )); } Console.WriteLine(" ");// sort and display the employee array
Employee.EmployeeComparer c = Employee.GetComparer( );
c.WhichComparison=Employee.EmployeeComparer.ComparisonType.EmpID;
empArray.Sort(c);
// display all the contents of the Employee array for (int i = 0;i<empArray.Count;i++) { Console.Write(" {0} ", empArray[i].ToString( )); } Console.WriteLine(" ");c.WhichComparison=Employee.EmployeeComparer.ComparisonType.Yrs;
empArray.Sort(c);
for (int i = 0;i<empArray.Count;i++) { Console.Write(" {0} ", empArray[i].ToString( )); } Console.WriteLine(" "); } } } Output: ID: 103. Years of Svc: 11 ID: 108. Years of Svc: 15 ID: 107. Years of Svc: 14 ID: 108. Years of Svc: 5 ID: 102. Years of Svc: 0 ID: 102. Years of Svc: 0 ID: 103. Years of Svc: 11 ID: 107. Years of Svc: 14 ID: 108. Years of Svc: 15 ID: 108. Years of Svc: 5 ID: 102. Years of Svc: 0 ID: 108. Years of Svc: 5 ID: 103. Years of Svc: 11 ID: 107. Years of Svc: 14 ID: 108. Years of Svc: 15
The first block of output shows the Employee
objects as they are added to the ArrayList
. The
employee ID values and the years of service are in random order.
The second block shows the results of sorting by the employee ID, and the third block shows the results of sorting by years of service.
52.15.129.90