Chapter 4. Arrays and Collections

IN THIS CHAPTER

Even if you are writing an application that works with ADO.NET and relational databases, you will still encounter situations where you need to know how to manage lists of information. Even if you aren’t the one who created the lists, you should still know how to use them. For example, there are many controls available in Windows Forms and Web Forms that expose lists or collections to allow you to manipulate their data.

This chapter introduces you to the basics of working with arrays and collections. You will learn the differences between the two types of data and the situations in which both are used.

Working with Arrays

Arrays are basically lists of sequentially accessible data. They provide a means of accessing and storing data in a way that allows multiple related elements to be manipulated via numeric index. This section shows you how to declare and use arrays within your code, including using multidimensional arrays and nested or jagged arrays.

Declaring and Initializing Arrays

To differentiate between a single value and an array value, the array indexer notation is used. This notation is represented by the square brackets [ and ]. To properly declare an array, you need to indicate the data type of the individual elements of the array. This data type is what precedes the [] notation in the sample array declarations that follow:

byte[] byteValues;
int[] counts;
string[] words;
Object[] objList;
Customer[] customers;

Unlike with some other languages, in C# you don’t specify the size of the array in the declaration portion. So, to specify an array of 12 elements, you cannot declare that as follows:

byte[12] byteArray;

Initializing the array is what provides C# with the boundary information on the array. Because arrays are essentially just specialized classes, you initialize them with the new keyword, as shown in the following samples:

byte[] byteArray = new byte[21];
int[] counters = new int[99];
string[] wordList = new string[21];

The preceding code initializes empty arrays out to a given length. These can then be populated programmatically from any number of sources, such as user input, XML files, or a relational database. Keep in mind that if you attempt to use an array that has not been initialized, you will get an error.

If you know the data that will be in the arrays at design time, you can pre-initialize the arrays with data as shown in the following samples:

byte[] byteArray = new byte[] { 1, 2, 3 };
byte[] byteArray2 = { 1, 2, 3 };
string[] wordList = { "The", "Quick", "Brown", "Fox" };

Using One-Dimensional Arrays

You can do many different things with a one-dimensional array. Because arrays are classes, you can press the “.” key at the end of any array variable to bring up IntelliSense and get a list of operations that are available to you for a given array, as shown in Figure 4.1.

Figure 4.1 IntelliSense for an Array object.

Image

Tables 4.1 and 4.2 describe some of the properties and methods that are provided by the Array class.

Table 4.1 Array Properties

Image

Table 4.2 Array Methods

Image

Take a look at the code in Listing 4.1, which illustrates how to make use of some of the methods and properties listed in Tables 4.1 and 4.2.

Listing 4.1 One-Dimensional Array Manipulation

Image

Image

The code for the FindAll and ForEach methods might appear to be a little odd if you are used to working with C# 1.1. Underneath those methods lies the power of Generics, which you will be introduced to in Chapter 6, “Introduction to Generics.” You don’t need to know every detail about how it works now because it will become clearer as you continue through the book. The good thing to know is that working with arrays in C# 2.0 is even easier than it was in the previous release.

Using Multidimensional Arrays

Multidimensional arrays are, as the name states, arrays with more than one dimension. This means that these arrays require multiple indices when accessing data contained within the array. Before taking a look at code that utilizes multidimensional arrays, you should be familiar with why you might want to use arrays with two, three, or even more dimensions.

One of the most common uses for 2D arrays is the storage of data in a Cartesian coordinate system (data that has an X and a Y coordinate location). These coordinates are found virtually everywhere you look in Windows Forms application because all controls have an X and a Y location.

So, assume that you had a grid that contained controls. You might access the control in the third column of the second row with the following code:

Control myControl = controlArray[2, 1];

Note that I used [2,1] to access the data instead of [3,2]. This is because arrays are zero-indexed, so the first element is at index 0, the second element is at index 1, and the third element is at index 2.

You can use two-dimensional arrays for any kind of data that can be expressed as a matrix (rectangular grid of data), such as the game board for a game of checkers or tic-tac-toe, or a spreadsheet containing sales values of various products organized by month.

Three-dimensional arrays are typically used to represent cube-shaped data. For example, you can use three-dimensional arrays to supply the information for Excel-style pivot tables, or you can use a 3D array to support three-dimensional location coordinates that include an X, Y, and Z location value.

Arrays with four or more dimensions can quickly become extremely difficult to maintain and are often extremely confusing when someone is attempting to read code involving arrays with four or more dimensions. These types of arrays are usually reserved for data with very specific requirements that are beyond the scope of this book. The rules for four-dimensional arrays are the same as the rules for two- and three-dimensional arrays, so your code will look similar.

You declare a multidimensional array by using a comma to indicate the presence of multiple dimensions in the array, as shown in the following example:

Image

The number of commas indicates the number of dimensions. As with single-dimension arrays, you cannot specify the maximum bounds of each dimension in the declaration; you can only specify it in the initialization. In addition, you may have noticed that there is no way to specify the minimum bounds. This is because in C#, all arrays start with the 0 index.

In the sample shown in Listing 4.2, you will see some code that works with two-dimensional arrays as well as one-dimensional arrays. The sample creates an array of month names and an array of product names. These arrays will be used as the labels for the rows and columns in a spreadsheet-style output that shows total product sales for each month. The product names will run vertically down the left side, and the month names run horizontally along the top. In the middle of the spreadsheet, the product sales values will be displayed using a two-dimensional array as the source.

Listing 4.2 Two-Dimensional Array Sample

Image

Image

The output of the preceding code looks as follows (with a few months cut out to make the display fit on a page):

Image

Using Jagged Arrays

The preceding section dealt with rectangular arrays, in which the number of columns (elements) would be the same for each row, giving you data that is shaped like a matrix. Jagged arrays are arrays whose elements are also arrays.

A jagged array (also referred to as an array of arrays) is an array (single-dimension or multidimensional) in which the elements are themselves arrays. This allows each element of the array to be an array with a different size.

There are many ways to declare jagged arrays, some of which take advantage of the same initializer shortcuts that are available for other array types. The following few lines of code declare a jagged array without doing any initialization:

string[][] jaggedStrings;
int[][] jaggedInts;
Customer[][] jaggedCustomers;

Note that instead of declaring it with the comma notation indicating a multidimensional array, the “Array of Arrays” notation is used, which is two array bracket symbols: [][].

The following lines of code illustrate a few of the ways in which you can initialize jagged arrays, including some of the shortcuts that allow you to omit the new keyword when providing the initial values for the array:

Image

As confusing as it might seem, you can mix jagged and rectangular arrays to create some powerful (and often difficult to decipher) structures. Take a look at the following code, which creates a jagged array in which each element is a different-size two-dimensional array:

Image

The preceding code declares the jagged array of two-dimensional arrays and will print the value 6. By accessing element 1 in the first dimension, the code is referencing the second multidimensional array. Then, the indexer [1,1] indicates the second pair of numbers, and the second number within that pair: 6.

The code in Listing 4.3 uses jagged arrays to display the list of the top-selling products during each month. The data here is fabricated, but real data would come from a relational data source such as SQL Server or an XML file (or XML from a web service) in a production application.

Listing 4.3 Jagged Array Demonstration

Image

One of the most important pieces of the preceding code is the looping through the arrays. Note that we don’t actually know the size of the array in each element of the first dimension of the jagged array. We have to obtain that length dynamically by checking:

productSales[month].Length

The other thing is that the preceding code won’t work if one of the elements in the first dimension is null, so your production code should have additional safety checks to prevent Null Reference exceptions. The output of the preceding code is as follows:

Image

Working with Collections

Collections are lists of data that don’t necessarily need to be accessed by numerical index. In many cases, collections have custom index types and some collections, such as the Queue and Stack, don’t support indexed access at all.

This section begins by comparing and contrasting the various features of arrays and collections and offers some advice on how to decide between the two. Then you will get an overview of some of the collections provided by the .NET Framework. This section will not cover Generic collections, as those will be addressed in Chapter 6, “Introduction to Generics.”

Comparing Arrays and Collections

All arrays are indexed with integers, regardless of whether they are jagged arrays, rectangular arrays, or one-dimensional arrays. One main deciding factor is that arrays are generally faster to use. The main reason for this is that arrays are designed to be fixed-length and they are also all numerically indexed. A numerically indexed list of data is much faster at sequential access than a list of data that is accessed by a long string key such as a GUID. Some collections can use arbitrary objects as their keys. The tradeoff is performance versus functionality. If the data you need to store can possibly be stored as an array of fixed length (meaning the length doesn’t change after initialization, not that you need to know the length at design time), that is definitely the way to go. However, you may need additional functionality provided by some of the classes described in the rest of the chapter. Collections are designed to store dynamic lists of data that can grow or shrink at runtime that may or may not be indexed numerically. If one of the Collection classes suits your needs, you may decide that it would be better to use that collection than to try to fit your data into an array.

One final point of comparison is the ability to resize. Although all arrays can be resized by invoking Array.Resize, the performance penalty is pretty high. When you invoke that method, a new array of the appropriate size is created, and then each element from the existing array is copied into the new array. Although it’s faster than doing this yourself manually, it still has a fairly high overhead, especially if you plan on resizing your array often. In short, arrays are generally of a fixed length while collections are designed to have a variable size.

Using the ArrayList

The ArrayList functions much like an array, with a few extremely powerful exceptions. The most notable of these is the fact that you can dynamically add and remove items from the list at runtime without incurring the fairly large penalty for regular array resizing. ArrayLists, just like regular arrays, are accessed via a zero-based integer index. When you create a new ArrayList, it starts with a capacity of 0 and 0 items in the list. You then dynamically add and remove items programmatically via methods on the ArrayList class. One other thing to keep in mind before diving into the code for the ArrayList class is that the ArrayList considers nulls valid (so you can add a null to the list), and you can add the same value more than once.

Before getting into the code, let’s take a look at some of the properties and methods of the ArrayList class in Tables 4.3 and 4.4.

Table 4.3 ArrayList Properties

Image

Table 4.4 ArrayList Methods

Image

Listing 4.4 illustrates the use of the ArrayList using strings as the element type.

Listing 4.4 ArrayList Demo

Image

Image

The output from this demo looks like this:

Image

Using the Hashtable

The Hashtable is a special form of collection that stores name-value pair combinations like a traditional dictionary. The Hashtable is optimized, however, in that the object used as the indexer is hashed for more efficient access. What that all boils down to is that the Hashtable is extremely efficient at retrieving single values with keys of arbitrary data types, but is only as efficient as the other collection classes at being enumerated. Although the Hashtable accepts any kind of object as the key, one of the most common uses of the Hashtable is using strings as indices for data, as shown in the example in Listing 4.5. The code in Listing 4.5 shows you two different ways to iterate through the values and keys stored in the Hashtable, as well as how to directly access the data, add new data, and query Hashtable properties.

Listing 4.5 Hashtable Demo

Image

Image

Using the Queue

The Queue is a special kind of collection. Unlike with most other collections, in Visual C# you don’t have direct access to all of the items within the Queue. The Queue is a first-in, first-out collection of items. You enqueue an item when you place it in the collection and you dequeue an item when you obtain the first item in the collection.

In several scenarios, the Queue class can be quite useful. If you are working with data that is handled in the order in which it was received, the Queue is the class to use.

You cannot access individual items in a Queue through an index, but you can obtain the number of items in the Queue with the Count property. Table 4.5 is a list of some common methods of the Queue class.

Table 4.5 Queue Methods

Image

Listing 4.6 illustrates the use of the Queue collection.

Listing 4.6 Queue Demo

Image

Image

Using the Stack

The Stack class is a simple last-in first-out (LIFO) collection of objects. It works very much like the queue, except that the last item to be added to the Stack is going to be the next item that will come off the stack. Listing 4.7 provides a quick demonstration of the features of the Stack class.

Listing 4.7 Stack Demo

Image

Using the SortedList

The SortedList is a name/value collection where the items are sorted by the keys, and you can access the data within the collection both by the object key and by numerical index. When you add an item to the collection, it is then sorted based on the key of the item, as shown in the following example:

Image

The output from the preceding code shows how the items have been reordered to be properly sorted:

Image

The SortedList class provides a simple, easy-to-use means by which you can sort a list of items. A lot of user interface controls for both Web and Windows provide their own sorting code, but knowing that you can rely on the SortedList class for your own sorting can come in handy.

Summary

This chapter has provided coverage of both arrays and collections. You should have a good idea of how to utilize single-dimension arrays, multidimensional arrays, and jagged arrays. In addition, you should be able to compare and contrast the features and drawbacks of using arrays or collections. This chapter also provided an overview of some of the stock collection classes that are included with the .NET Framework, such as the ArrayList, SortedList, Queue, Stack, and more. As you continue through the book, you will be able to use the information in this chapter to utilize the powerful features of both arrays and collections in creating professional applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.140.195.28