Arrays


I have mentioned arrays several times in this book. An array is a fixed-size, contiguous set of slots in memory. Unlike an ArrayList or other collection object, an array can fill up, preventing you from adding more elements. The only way to add elements to a full array is to create a second larger array and copy all elements from the first array to the second array.

Java provides special syntactical support for arrays. Indirectly, you have been using arrays all along, in the form of the class ArrayList. An ArrayList stores all elements you add to it in a Java array. The ArrayList class also encapsulates the tedium of having to grow the array when you add too many elements.

The example here creates a new class named Performance, which CourseSession might use to track test scores for each student. The Performance class allows calculating the average across all tests.


 package sis.studentinfo; import junit.framework.*; public class PerformanceTest extends TestCase {    private static final double tolerance = 0.005;    public void testAverage() {       Performance performance = new Performance();       performance.setNumberOfTests(4);       performance.set(0, 98);       performance.set(1, 92);       performance.set(2, 81);       performance.set(3, 72);       assertEquals(92, performance.get(1));       assertEquals(85.75, performance.average(), tolerance);    } } 

The Performance class:

 package sis.studentinfo; public class Performance {    private int[] tests;    public void setNumberOfTests(int numberOfTests) {       tests = new int[numberOfTests];    }    public void set(int testNumber, int score) {       tests[testNumber] = score;    }    public int get(int testNumber) {       return tests[testNumber];    }    public double average() {       double total = 0.0;       for (int score: tests)          total += score;       return total / tests.length;    } } 

You store the scores for the tests in an int array named tests:

 private int[] tests; 

This declaration only says that the instance variable tests is of type int[] (you read this as "int array"). The braces are the indicator that this is an array. You use the braces to subscript, or index, the array. By subscripting the array, you tell Java the index of the element with which you wish to work.

Java allows you to declare an array with the braces following the variable.

 private int tests[]; // don't declare arrays this way 

Avoid this construct. Use of it will brand you as a C/C++ programmer (a fate worse than death in Java circles).

Neither of the two array declarations above allocate any memory. You will receive an error[9] if you reference any of its (nonexistent) elements.

[9] Specifically, a NullPointerException. See Lesson 8 for more information on exceptions.

When a Performance client calls setNumberOfTests, the assignment statement initializes the test array.

 public void setNumberOfTests(int numberOfTests) {    tests = new int[numberOfTests]; } 

You allocate an array using the new keyword. After the new keyword, you specify the type (int in this example) that the array will hold and the number of slots to allocate (numberOfTests).

Once you initialize an array, you can set a value into any of its slots. The type of the value must match the type of the array. For example, you can only set int values into an int[].

 public void set(int testNumber, int score) {    tests[testNumber] = score; } 

The line of code in set assigns the value of score to the slot in the tests array that corresponds to the testNumber. Indexing of arrays is 0-based. The first slot is slot #0, the second slot #1, and so on.

The get method demonstrates accessing an element in an array at a particular index:

 public int get(int testNumber) {    return tests[testNumber]; } 

You can iterate an array like any other collection by using a for-each loop.

 public double average() {    double total = 0.0;    for (int score: tests)       total += score;    return total / tests.length; } 

The average method also shows how you may access the number of elements in an array by using a special variable named length.

You can iterate an array using a classic for loop:

 public double average() {    double total = 0.0;    for (int i = 0; i < tests.length; i++)       total += tests[i];    return total / tests.length; } 

You may declare an array as any type, reference or primitive. You might have an instance variable representing an array of Students:

 private Student[] students; 

Array Initialization

When you allocate an array, Java initializes its elements to the default value for the type. Java initializes all slots in an array of numerics to 0, to false in an array of booleans, and to null in an array of references.

Java provides special array initialization syntax that you can use at the time of array instantiation and/or declaration.

 public void testInitialization() {    Performance performance = new Performance();    performance.setScores(75, 72, 90, 60);    assertEquals(74.25, performance.average(), tolerance); } 

The setScores method passes each of the four score arguments into an array initializer.

 public void setScores(int score1, int score2, int score3, int score4) {    tests = new int[] { score1, score2, score3, score4 }; } 

The array initializer in this example produces an array with four slots, each populated with an int value.

A shortcut that you can use as part of an array declaration assignment statement is:

 int[] values = {1, 2, 3 }; 

You may terminate the list of values in an array initializer with a comma. The Java compiler ignores the final comma (it does not create an additional slot in the array). For example, the preceding declaration and initialization is equivalent to:

 int[] values = {   1,   2,   3, }; assertEquals(3, values.length); 

I coded the initialization on multiple lines to help demonstrate why this feature might be useful. It allows you to freely add to, remove from, and move elements around in the array initialization. You need not worry about leaving a comma after the last element; you can terminate each element with a comma.

When to Use Arrays

Prefer use of Java reference-type collections over native arrays. Your code will be simpler, closer to pure OO, and more flexible. It is easy to replace an ArrayList with a LinkedList, but it can be very costly to have to replace a native array with a LinkedList.

Some situations will force you into using arrays. Many existing APIs either return arrays or require arrays as arguments.

There are circumstances where arrays are more appropriate. Many mathematical algorithms and constructs (matrices, for example) are better implemented with arrays.

Accessing an element in an array is a matter of knowing which numbered slot it's in, multiplying by the slot size, and adding to some memory offset. The pseudomemory diagram in Figure 7.1 shows a three-element int array. Java stores the starting location of the array in this example as 0x100. To access the third element of the array (x[2]), Java calculates the new memory location as:

 0x100 starting address + 2nd slot * 4 bytes per slot 

Figure 7.1. An Array in Memory


Each slot requires four bytes, since it takes four bytes to represent an int.[10] The result is the address 0x108, where the element 55 can be found.

[10] Java stores primitive types directly in the array. An array of objects would store a contiguous list of references to other locations in memory.

If you need the absolute fastest performance possible, an array provides a performance improvement over a collection class. However, prefer the use of collection classes over arrays. Always measure performance before arbitrarily converting use of a collection class to an array.

Prefer object-oriented collections over arrays.


Varargs

If you want to pass a variable number of similar-typed arguments to a method, you must first combine them into a collection. The setScores method above is restricted to four test scores; you would like to be able to pass any number of scores into a Performance object. Prior to J2SE 5.0, a standard idiom for doing so was to declare a new array on the fly:

 public void testArrayParm() {    Performance performance = new Performance();    performance.setScores(new int[] {75, 72, 90, 60 });    assertEquals(74.25, performance.average(), tolerance); } 

The setScores method can then store the array directly:

 public void setScores(int[] tests) {    this.tests = tests; } 

You might try to use the shorter array declaration/initialization syntax:

 performance.setScores({ 75, 72, 90, 60 }); // this will not compile 

but Java does not allow it.

J2SE 5.0 allows you to specify that a method takes a variable number of arguments. The arguments must appear at the end of the method argument list and must all be of the same type.

You declare the variability of a parameter with ellipses (...). Programmers familiar with C refer to this as a varargs declaration.

 public void setScores(int... tests) {    this.tests = tests; } 

Method calls to setScores can now have a variable number of test scores:

 public void testVariableMethodParms() {    Performance performance = new Performance();    performance.setScores(75, 72, 90, 60);    assertEquals(74.25, performance.average(), tolerance);    performance.setScores(100, 90);    assertEquals(95.0, performance.average(), tolerance); } 

When you execute a Java application, the VM passes command-line arguments to the main method as an array of String objects:

 public static void main(String[] args) 

You can declare the main method too as:

 public static void main(String... args) 

Multidimensional Arrays

Java also supports arrays of arrays, also known as multidimensional arrays. A classic use of multidimensional arrays is to represent matrices. You can create arrays that have up to 255 dimensions, but you will probably never need more than 3 dimensions. The following language test demonstrates how you allocate, populate, and access a two-dimensional array.

 // 0 1  2  3 // 4 5  6  7 // 8 9 10 11 public void testTwoDimensionalArrays() {    final int rows = 3;    final int cols = 4;    int count = 0;    int[][] matrix = new int[rows][cols];    for (int x = 0; x < rows; x++)       for (int y = 0; y < cols; y++)          matrix[x][y] = count++;    assertEquals(11, matrix[2][3]);    assertEquals(6, matrix[1][2]); } 

You do not need to allocate all dimensions at once. You can allocate dimensions starting at the left, leaving the rightmost dimensions unspecified. Later, you can allocate these rightmost dimensions. In testPartialDimensions, the code initially allocates matrix as an int array of three rows. Subsequently, it allocates each slot in the rows portion of the array to differing-sized int arrays of columns.

 // 0 // 1 2 // 3 4 5 public void testPartialDimensions() {    final int rows = 3;    int[][] matrix = new int[rows][];    matrix[0] = new int[]{0 };    matrix[1] = new int[]{1, 2 };    matrix[2] = new int[]{3, 4, 5 };    assertEquals(1, matrix[1][0]);    assertEquals(5, matrix[2][2]); } 

As shown in testPartialDimensions, Java does not restrict you to rectangular multidimensional arrays; you can make them jagged.

You can initialize multidimensional arrays using array initialization syntax. The following initialization produces an array that is equivalent to the array initialized by testPartialDimensions:

 int[][] matrix2 = {{0 }, {1, 2 }, {3, 4, 5 }}; 

The Arrays Class

Since arrays are not objects, you cannot send messages to them. You can access the variable length to determine the size of an array, but Java provides it as special syntax.

The class java.util.Arrays gives you a number of class methods to perform common operations on arrays. Using the Arrays class, you can do the following with a single-dimensional array:

  • perform binary searches (which assumes the array is in sorted order) (binarySearch)

  • sort it (sort)

  • convert it to an object that implements the List interface (asList)

  • compare it to another single-dimensional array of the same type (equals)

  • obtain a hash code (see Lesson 9) (hashCode)

  • fill each of its elements, or a subrange of its elements, with a specified value (fill)

  • obtain a printable representation of the array (toString)

I'll discuss the equals method in more depth. For more information about the other methods, refer to the Java API documentation for more information on java.util.Arrays.

Arrays.equals

An array is a reference type, regardless of whether you declare it to contain primitives or references. If you create two separate arrays, the Java VM will store them in separate memory locations. This means that comparing the two arrays with == will result in false.

 public void testArrayEquality() {    int[] a = {1, 2, 3 };    int[] b = {1, 2, 3 };    assertFalse(a == b); } 

Since an array is a reference, you can compare two arrays using the equals method. But even if you allocate both arrays to exactly same dimensions and populate them with exactly the same contents, the comparison will return false.

 public void testArrayEquals() {    int[] a = {1, 2, 3 };    int[] b = {1, 2, 3 };    assertFalse(a.equals(b)); } 

You can use the Arrays.equals method to compare the contents, not the memory location, of two arrays.

 public void testArraysEquals() {    int[] a = {1, 2, 3 };    int[] b = {1, 2, 3 };    assertTrue(Arrays.equals(a, b)); } 



Agile Java. Crafting Code with Test-Driven Development
Agile Javaв„ў: Crafting Code with Test-Driven Development
ISBN: 0131482394
EAN: 2147483647
Year: 2003
Pages: 391
Authors: Jeff Langr

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net