Chapter 7. Arrays and Array Lists

CHAPTER GOALS

  • To become familiar with using arrays and array lists

  • To learn about wrapper classes, auto-boxing, and the enhanced for loop

  • To study common array algorithms

  • To learn how to use two-dimensional arrays

  • To understand when to choose array lists and arrays in your programs

  • To implement partially filled arrays

    T To understand the concept of regression testing

In order to process large quantities of data, you need to have a mechanism for collecting values. In Java, arrays and array lists serve this purpose. In this chapter, you will learn how to construct arrays and array lists, fill them with values, and access the stored values. We introduce the enhanced for loop, a convenient statement for processing all elements of a collection. You will see how to use the enhanced for loop, as well as ordinary loops, to implement common array algorithms. The chapter concludes with a discussion of two-dimensional arrays, which are useful for handling rows and columns of data.

Arrays

In many programs, you need to manipulate collections of related values. It would be impractical to use a sequence of variables such as value1, value2, value3, . . ., and so on. The array construct provides a better way of storing a collection of values.

An array is a sequence of values of the same type. The values that are stored in an array are called its "elements". For example, here is how you construct an array of 10 floating-point numbers:

new double[10]

Note

An array is a sequence of values of the same type.

The number of elements (here, 10) is called the length of the array.

The new operator merely constructs the array. You will want to store a reference to the array in a variable so that you can access it later.

The type of an array variable is the element type, followed by []. In this example, the type is double[], because the element type is double. Here is the declaration of an array variable:

double[] values = new double[10];

That is, values is a reference to an array of floating-point numbers. It is initialized with an array of 10 numbers (see Figure 1).

You can also form arrays of objects, for example

BankAccount[] accounts = new BankAccount[10];

When an array is first created, all elements are initialized with 0 (for an array of numbers such as int[] or double[]), false (for a boolean[] array), or null (for an array of object references).

An Array Reference and an Array

Figure 7.1. An Array Reference and an Array

Alternatively, you can initialize an array with other values. List all elements that you want to include in the array, enclosed in braces and separated by commas:

int[] primes = { 2, 3, 5, 7, 11 };

The Java compiler counts how many elements you want to place in the array, allocates an array of the correct size, and fills it with the elements that you specify.

Each element in the array is specified by an integer index that is placed inside square brackets ([]). For example, the expression

values[4]

denotes the element of the values array with index 4.

You can store a value at a location with an assignment statement, such as the following.

values[2] = 29.95;

Now the position with index 2 of values is filled with 29.95 (see Figure 2).

To read the element at index 2, simply use the expression values[2] as you would any variable of type double:

System.out.println("The element at index 2 is " + values[2]);

Note

You access an array element with an integer index, using the [] operator.

If you look closely at Figure 2, you will notice that the index values start at 0. That is,

values[0] is the first element
values[1] is the second element
values[2] is the third element
Modifying an Array Element

Figure 7.2. Modifying an Array Element

and so on. This convention can be a source of grief for the newcomer, so you should pay close attention to the index values. In particular, the last element in the array has an index one less than the array length. For example, values refers to an array with length 10. The last element is values[9].

Note

Index values of an array range from 0 to length - 1.

If you try to access an element that does not exist, then an "array index out of bounds" exception occurs. For example, the statement

values[10] = 29.95; // ERROR

is a bounds error.

Note

Accessing a nonexistent element results in a bounds error.

To avoid bounds errors, you will want to know how many elements are in an array. The expression

values.length

is the length of the values array. Note that there are no parentheses following length—it is an instance variable of the array object, not a method. However, you cannot modify this instance variable. In other words, length is a final public instance variable. This is quite an anomaly. Normally, Java programmers use a method to inquire about the properties of an object. You just have to remember to omit the parentheses in this case.

Note

The expression array. length yields the number of elements in an array.

The following code ensures that you only access the array when the index variable i is within the legal bounds:

if (0 <= i && i < values.length) values[i] = value;

Arrays suffer from a significant limitation: their length is fixed. If you start out with an array of 10 elements and later decide that you need to add additional elements, then you need to make a new array and copy all elements of the existing array into the new array. We will discuss this process in detail in Section 7.6.

Table 7.1. Declaring Arrays

int[] numbers = new int[10];

An array of ten integers. All elements are initialized with zero.

final int NUMBERS_LENGTH = 10;
int[] numbers = new int[NUMBERS_LENGTH];

It is a good idea to use a named constant instead of a "magic number".

int valuesLength = in.nextInt();
double[] values = new double[valuesLength];

The length need not be a constant.

int[] squares = { 0, 1, 4, 9, 16 };

An array of five integers, with initial values.

String[] names = new String[3];

An array of three string references, all initially null.

String[] friends = { "Emily", "Bob", "Cindy" };

Another array of three strings.

double[] values = new int[10]

Error: You cannot initialize a double[] variable with an array of type int[].

Arrays
double[] values = new double[10];
for (int i = 0; i < values.length; i++) values[i] = i * i;

What do the following program segments print? Or, if there is an error, describe the error and specify whether it is detected at compile-time or at run-time.

  1. double[] a = new double[10];
    System.out.println(a[0]);
  2. double[] b = new double[10];
    System.out.println(b[10]);
  3. double[] c;
    System.out.println(c[0]);

Array Lists

The array construct is rather primitive. In this section, we introduce the ArrayList class. It lets you collect objects, just like an array does, but array lists offer two significant benefits:

  • Array lists can grow and shrink as needed.

  • The ArrayList class supplies methods for many common tasks, such as inserting and removing elements.

Note

The ArrayList class manages a sequence of objects whose size can change.

You declare an array list of strings as follows:

ArrayList<String> names = new ArrayList<String>();

The type ArrayList<String> denotes an array list of strings. The angle brackets around the String type tell you that String is a type parameter. You can replace String with any other class and get a different array list type. For that reason, ArrayList is called a generic class. You will learn more about generic classes in Chapter 17. For now, simply use an ArrayList<T> whenever you want to collect objects of type T. However, keep in mind that you cannot use primitive types as type parameters—there is no ArrayList<int> or ArrayList<double>. You will see in Section 7.4 how to overcome that limitation.

Note

The ArrayList class is a generic class: ArrayList<TypeName> collects objects of the given type.

When you construct an ArrayList object, it has size 0. You use the add method to add an object to the end of the array list. The size increases after each call to add (see Figure 5). The size method yields the current size of the array list.

Adding an Element with add

Figure 7.5. Adding an Element with add

names.add("Emily"); // Now names has size 1 and element "Emily"
names.add("Bob"); // Now names has size 2 and elements "Emily", "Bob"
names.add("Cindy"); // names has size 3 and elements "Emily", "Bob", and "Cindy"

To obtain the value of an array list element, use the get method, not the [ ] operator. As with arrays, index values start at 0. For example, names.get(2) retrieves the element with index 2, the third element in the array list:

String name = names.get(2);

As with arrays, it is an error to access a nonexistent element. A very common bounds error is to use the following:

int i = names.size();
name = names.get(i); // Error

The last valid index is names.size() - 1.

To set an array list element to a new value, use the set method.

names.set(2, "Carolyn");

This call sets position 2 of the names array list to "Carolyn", overwriting whatever value was there before.

Adding and Removing Elements in the Middle of an Array List

Figure 7.6. Adding and Removing Elements in the Middle of an Array List

The set method can only overwrite existing values. It is different from the add method, which adds a new object to the end of the array list.

You can also insert an object in the middle of an array list. The call names.add(1, "Ann") moves all elements with index 1 or larger by one position and adds the string "Ann" at index 1 (see Figure 6). After each call to the add method, the size of the array list increases by 1.

Conversely, the remove method removes the element at a given index, moves all elements after the removed element to the next lower index, and reduces the size of the array list by 1. Part 3 of Figure 6 illustrates the call names.remove(1).

The following program demonstrates how to use ArrayList class for collecting BankAccount objects. The BankAccount class has been enhanced from the version in Chapter 3. Each bank account has an account number. Note that you import the generic class java.util.ArrayList, without the type parameter.

Table 7.2. Working with Array Lists

ArrayList<String> names = new ArrayList<String>();

Constructs an empty array list that can hold strings.

names.add("Ann");
names.add("Cindy");

Adds elements to the end.

System.out.println(names);

Prints [Ann, Cindy].

names.add(1, "Bob");

Inserts an element at index 1. names is now [Ann, Bob, Cindy].

names.remove(0);

Removes the element at index 0. names is now [Bob, Cindy].

names.set(0, "Bill");

Replaces an element with a different value. names is now [Bill, Cindy].

String name = names.get(i);

Gets an element.

String last = names.get(names.size() - 1);

Gets the last element.

ArrayList<Integer> squares = new ArrayList<Integer>();
for (int i = 0; i < 10; i++)
{
   squares.add(i * i);
}

Constructs an array list holding the first ten squares.

ch07/arraylist/ArrayListTester.java

1 import java.util.ArrayList;
2
3 /**
4    This program tests the ArrayList class.
5 */
6 public class ArrayListTester
7 {
8    public static void main(String[] args)
9     {
10       ArrayList<BankAccount> accounts = new ArrayList<BankAccount>();
11       accounts.add(new BankAccount(1001));
12       accounts.add(new BankAccount(1015));
13       accounts.add(new BankAccount(1729));
14       accounts.add(1, new BankAccount(1008));
15       accounts.remove(0);
16
17       System.out.println("Size: " + accounts.size());
18       System.out.println("Expected: 3");
19       BankAccount first = accounts.get(0);
20       System.out.println("First account number: "
21              + first.getAccountNumber());
22       System.out.println("Expected: 1008");
23       BankAccount last = accounts.get(accounts.size() - 1);
24       System.out.println("Last account number: "
25             + last.getAccountNumber());
26       System.out.println("Expected: 1729");
27     }
28 }

ch07/arraylist/BankAccount.java

1 /**
2    A bank account has a balance that can be changed by
3    deposits and withdrawals.
4 */
5 public class BankAccount
6 {
7    private int accountNumber;
8    private double balance;
9
10    /**
11       Constructs a bank account with a zero balance.
12       @param anAccountNumber the account number for this account
13    */
14    public BankAccount(int anAccountNumber)
15    {
16        accountNumber = anAccountNumber;
17       balance = 0;
18    }
19
20    /**
21       Constructs a bank account with a given balance.
22       @param anAccountNumber the account number for this account
23       @param initialBalance the initial balance
24    */
25    public BankAccount(int anAccountNumber, double initialBalance)
26    {
27       accountNumber = anAccountNumber;
28       balance = initialBalance;
29    }
30
31    /**
32       Gets the account number of this bank account.
33       @return the account number
34    */
35    public int getAccountNumber()
36    {
37        return accountNumber;
38    }
39
40    /**
41       Deposits money into the bank account.
42       @param amount the amount to deposit
43    */
44    public void deposit(double amount)
45    {
46        double newBalance = balance + amount;
47        balance = newBalance;
48    }
49
50    /**
51       Withdraws money from the bank account.
52       @param amount the amount to withdraw
53    */
54     public void withdraw(double amount)
55    {
56        double newBalance = balance - amount;
57        balance = newBalance;
58    }
59
60    /**
61        Gets the current balance of the bank account.
62        @return the current balance
63    */
64    public double getBalance()
65    {
66        return balance;
67    }
68 }

Program Run

Size: 3
Expected: 3
First account number: 1008
Expected: 1008
Last account number: 1729
Expected: 1729
Working with Array Lists

4. What is the content of names after the following statements?

ArrayList<String> names = new ArrayList<String>();
names.add("A");
names.add(0, "B");
names.add("C");
names.remove(1);

Data Type

Number of Elements

Array

a.length

Array list

a.size()

String

a.length()

Wrappers and Auto-boxing

Because numbers are not objects in Java, you cannot directly insert them into array lists. For example, you cannot form an ArrayList<double>. To store sequences of numbers in an array list, you must turn them into objects by using wrapper classes. There are wrapper classes for all eight primitive types:

Note

To treat primitive type values as objects, you must use wrapper classes.

Primitive Type

Wrapper Class

byte

Byte

boolean

Boolean

char

Character

double

Double

float

Float

int

Integer

long

Long

short

Short

Note that the wrapper class names start with uppercase letters, and that two of them differ from the names of the corresponding primitive type: Integer and Character.

Each wrapper class object contains a value of the corresponding primitive type. For example, an object of the class Double contains a value of type double (see Figure 7).

An Object of a Wrapper Class

Figure 7.7. An Object of a Wrapper Class

Wrapper objects can be used anywhere that objects are required instead of primitive type values. For example, you can collect a sequence of floating-point numbers in an ArrayList<Double>.

Conversion between primitive types and the corresponding wrapper classes is automatic. This process is called auto-boxing (even though auto-wrapping would have been more consistent).

For example, if you assign a number to a Double object, the number is automatically "put into a box", namely a wrapper object.

Double d = 29.95; // Auto-boxing; same as Double d = new Double(29.95);

Conversely, wrapper objects are automatically "unboxed" to primitive types.

double x = d; // Auto-unboxing; same as double x = d.doubleValue();

Auto-boxing even works inside arithmetic expressions. For example, the statement

d = d + 1;

is perfectly legal. It means:

  • Auto-unbox d into a double

  • Add 1

  • Auto-box the result into a new Double

  • Store a reference to the newly created wrapper object in d

In order to collect numbers in an array list, simply remember to use the wrapper type as the type parameter, and then rely on auto-boxing.

ArrayList<Double> values = new ArrayList<Double>();
values.add(29.95);
double x = values.get(0);

Keep in mind that storing wrapped numbers is quite inefficient. The use of wrappers is acceptable if you only collect a few numbers, but you should use arrays for long sequences of numbers or characters.

An Object of a Wrapper Class

6. Suppose values is an ArrayList<Double> of size > 0. How do you increment the element with index 0?

The Enhanced for Loop

Java version 5.0 introduces a very convenient shortcut for a common loop type. Often, you need to iterate through a sequence of elements—such as the elements of an array or array list. The enhanced for loop makes this process particularly easy to program.

Suppose you want to total up all elements in an array values. Here is how you use the enhanced for loop to carry out that task.

double[] values = ...;
double sum = 0;
for (double element : values)
{
   sum = sum + element;
}

Note

The enhanced for loop traverses all elements of a collection.

The loop body is executed for each element in the array values. At the beginning of each loop iteration, the next element is assigned to the variable element. Then the loop body is executed. You should read this loop as "for each element in values".

You may wonder why Java doesn't let you write "for each (element in values)". Unquestionably, this would have been neater, and the Java language designers seriously considered this. However, the "for each" construct was added to Java several years after its initial release. Had new reserved words each and in been added to the language, then older programs that happened to use those identifiers as variable or method names (such as System.in) would no longer have compiled correctly.

You don't have to use the "for each" construct to loop through all elements in an array. You can implement the same loop with a straightforward for loop and an explicit index variable:

double[] values = ...;
double sum = 0;
for (int i = 0; i < values.length; i++)
{
   double element = values[i];
   sum = sum + element;
}

Note an important difference between the "for each" loop and the ordinary for loop. In the "for each" loop, the loop variable e is assigned elements: values[0], values[1], and so on. In the ordinary for loop, the loop variable i is assigned index values: 0, 1, and so on.

Note

In an enhanced for loop, the loop variable contains an element, not an index.

You can also use the enhanced for loop to visit all elements of an array list. For example, the following loop computes the total of the balances of all accounts:

ArrayList<BankAccount> accounts = ... ;
double sum = 0;
for (BankAccount account : accounts)
{
   sum = sum + account.getBalance();
}

This loop is equivalent to the following ordinary for loop:

double sum = 0;
for (int i = 0; i < accounts.size(); i++)
{
   BankAccount account = accounts.get(i);
   sum = sum + account.getBalance();
}

Keep in mind that the "for each" loop has a very specific purpose: getting the elements of a collection, from the beginning to the end. It is not suitable for all array algorithms. In particular, the "for each" loop does not allow you to modify the contents of an array. The following loop does not fill an array with zeroes:

for (double element : values)
{
   element = 0; // ERROR--this assignment does not modify array elements
}

When the loop is executed, the variable element is first set to values[0]. Then element is set to 0, then to values[1], then to 0, and so on. The values array is not modified. The remedy is simple: Use an ordinary for loop

for (int i = 0; i < values.length; i++)
{
   values[i] = 0; // OK
}
The "for each" Loop

What does this "for each" loop do?

int counter = 0;
for (BankAccount a : accounts)
{
   if (a.getBalance() == 0) { counter++; }
}

Partially Filled Arrays

Suppose you write a program that reads a sequence of numbers into an array. How many numbers will the user enter? You can't very well ask the user to count the items before entering them—that is just the kind of work that the user expects the computer to do. Unfortunately, you now run into a problem. You need to set the size of the array before you know how many elements you need. Once the array size is set, it cannot be changed.

To solve this problem, make an array that is guaranteed to be larger than the largest possible number of entries, and partially fill it. For example, you can decide that the user will never provide more than 100 input values. Then allocate an array of size 100:

final int VALUES_LENGTH = 100;
double[] values = new double[VALUES_LENGTH];

Then keep a companion variable that tells how many elements in the array are actually used. It is an excellent idea always to name this companion variable by adding the suffix Size to the name of the array.

int valuesSize = 0;

Note

With a partially filled array, keep a companion variable to track how many elements are used.

Now values.length is the capacity of the array values, and valuesSize is the current size of the array (see Figure 8). Keep adding elements into the array, incrementing the valuesSize variable each time.

values[valuesSize] = x;
valuesSize++;

This way, valuesSize always contains the correct element count.

The following code segment shows how to read numbers into a partially filled array.

int valuesSize = 0;
Scanner in = new Scanner(System.in);
while (in.hasNextDouble())
{
   if (valuesSize < values.length)
   {
      values[valuesSize] = in.nextDouble();
      valuesSize++;
   }
}

At the end of this loop, valuesSize contains the actual number of elements in the array. Note that you have to stop accepting inputs if the valuesSize companion variable reaches the array length. Section 7.6 shows how you can overcome that limitation by growing the array.

A Partially Filled Array

Figure 7.8. A Partially Filled Array

To process the gathered array elements, you again use the companion variable, not the array length. This loop prints the partially filled array:

for (int i = 0; i < valuesSize; i++)
{
   System.out.println(values[i]);
}

Array lists use this technique behind the scenes. An array list contains an array of objects. When the array runs out of space, the array list allocates a larger array and copies the elements. However, all of this happens inside the array list methods, so you never need to think about it.

A Partially Filled Array

10. How do you remove the last element of the partially filled array values?

11. Why would a programmer use a partially filled array of numbers instead of an array list?

Common Array Algorithms

In the following sections, we discuss some of the most common algorithms for working with arrays and array lists.

In the examples, we show a mixture of arrays and array lists so that you become familiar with the syntax for both constructs.

Filling

This loop fills an array with zeroes:

for (int i = 0; i < values.length; i++)
{
   values[i] = 0;
}

Here, we fill an array list with squares (0, 1, 4, 9, 16, . . .). Note that the element with index 0 contains 02, the element with index 1 contains 12, and so on.

for (int i = 0; i < values.size(); i++)
{
   values.set(i, i * i);
}

Computing Sum and Average Values

To compute the sum of all elements, simply keep a running total.

double total = 0;
for (double element : values)
{
   total = total + element;
}

To obtain the average, divide by the number of elements:

double average = total / values.size(); // For an array list

Be sure to check that the size is not zero.

Counting Matches

Suppose you want to find how many accounts of a certain type you have. Then you must go through the entire collection and increment a counter each time you find a match. Here we count the number of accounts whose balance is at least as much as a given threshold:

public class Bank
{
   private ArrayList<BankAccount> accounts;

   public int count(double atLeast)
   {
      int matches = 0;
      for (BankAccount account : accounts)
      {
         if (account.getBalance() >= atLeast) matches++; // Found a match
      }
      return matches;
   }
   ...
}

Note

To count values, check all elements and count the matches until you reach the end.

Finding the Maximum or Minimum

Suppose you want to find the account with the largest balance in the bank. Keep a candidate for the maximum. If you find an element with a larger value, then replace the candidate with that value. When you have reached the end of the sequence, you have found the maximum.

There is just one problem. When you visit the starting element, you don't yet have a candidate for the maximum. One way to overcome that is to set the candidate to the starting element and make the first comparison with the next element.

BankAccount largestYet = accounts.get(0);
for (int i = 1; i < accounts.size(); i++)
{
   BankAccount a = accounts.get(i);
   if (a.getBalance() > largestYet.getBalance())
      largestYet = a;
}
return largestYet;

Note

To compute the maximum or minimum value, initialize a candidate with the starting element. Then compare the candidate with the remaining elements and update it if you find a larger or smaller value.

Here we use an explicit for loop because the loop no longer visits all elements—it skips the starting element.

Of course, this approach works only if there is at least one element. It doesn't make a lot of sense to ask for the largest element of an empty collection. We can return null in that case:

if (accounts.size() == 0) return null;
BankAccount largestYet = accounts.get(0);
...

See Exercises R7.5 and R7.6 for slight modifications to this algorithm.

To compute the minimum of a data set, keep a candidate for the minimum and replace it whenever you encounter a smaller value. At the end of the sequence, you have found the minimum.

Searching for a Value

Suppose you want to know whether there is a bank account with a particular account number in your bank. Simply inspect each element until you find a match or reach the end of the sequence. Note that the loop might fail to find an answer, namely if none of the accounts match. This search process is called a linear search.

public class Bank
{
    ...
   public BankAccount find(int accountNumber)
   {
      for (BankAccount account : accounts)
      {
         if (account.getAccountNumber() == accountNumber) // Found a match
            return account;
      }
      return null; // No match in the entire array list
   }
   ...
}

Note

To find a value, check all elements until you have found a match.

Note that the method returns null if no match is found.

Locating the Position of an Element

You often need to locate the position of an element so that you can replace or remove it. Use a variation of the linear search algorithm, but remember the position instead of the matching element. Here we locate the position of the first element that is larger than 100.

int pos = 0;
boolean found = false;
while (pos < values.size() && !found)
{
   if (values.get(pos) > 100)
   {
      found = true;
   }
   else
   {
      pos++;
   }
}
if (found) { System.out.println("Position: " + pos); }
else { System.out.println("Not found"); }

Removing an Element

Removing an element from an array list is very easy—simply use the remove method. With an array, you have to work harder.

Suppose you want to remove the element with index pos from the array values. First off, you need to keep a companion variable for tracking the number of elements in the array, as explained in Section 7.5.

If the elements in the array are not in any particular order, simply overwrite the element to be removed with the last element of the array, then decrement the variable tracking the size of the array. (See Figure 9.)

values[pos] = values[valuesSize - 1];
valuesSize--;

The situation is more complex if the order of the elements matters. Then you must move all elements following the element to be removed to a lower index, and then decrement the variable holding the size of the array. (See Figure 10.)

Removing an Element in an Unordered Array

Figure 7.9. Removing an Element in an Unordered Array

Removing an Element in an Ordered Array

Figure 7.10. Removing an Element in an Ordered Array

for (int i = pos; i < valuesSize - 1; i++)
{
   values[i] = values[i + 1];
}
valuesSize--;

Inserting an Element

To insert an element into an array list, simply use the add method.

In this section, you will see how to insert an element into an array. Note that you need a companion variable for tracking the array size, as explained in Section 7.5. If the order of the elements does not matter, you can simply insert new elements at the end, incrementing the variable tracking the size.

if (valuesSize < values.length)
{
   values[valuesSize] = newElement;
   valuesSize++;
}

It is more work to insert an element at a particular position in the middle of an array. First, move all elements above the insertion location to a higher index. Then insert the new element.

Note the order of the movement: When you remove an element, you first move the next element down to a lower index, then the one after that, until you finally get to the end of the array. When you insert an element, you start at the end of the array, move that element to a higher index, then move the one before that, and so on until you finally get to the insertion location (see Figure 12).

if (valuesSize < values.length)
{
   for (int i = valuesSize; i > pos; i--)
   {
      values[i] = values[i - 1];
   }
   values[pos] = newElement;
   valuesSize++;
}
Inserting an Element in an Unordered Array

Figure 7.11. Inserting an Element in an Unordered Array

Inserting an Element in an Ordered Array

Figure 7.12. Inserting an Element in an Ordered Array

Copying and Growing Arrays

Array variables work just like object variables—they hold a reference to the actual array. If you copy the reference, you get another reference to the same array (see Figure 13):

double[] values = new double[6];
. . . // Fill array
double[] prices = values;
Copying and Growing Arrays

Note

An array variable stores a reference to the array. Copying the variable yields a second reference to the same array.

If you want to make a true copy of an array, call the Arrays.copyOf method.

double[] prices = Arrays.copyOf(values, values.length);
Copying and Growing Arrays

Note

Use the Arrays.copyOf method to copy the elements of an array.

Another use for Arrays.copyOf is to grow an array that has run out of space. The following statement has the effect of doubling the length of an array:

values = Arrays.copyOf(values, 2 * values.length);

See Figure 14.

For example, here is how you can read an arbitrarily long sequence numbers into an array, without running out of space:

int valuesSize = 0;
while (in.hasNextDouble())
{
   if (valuesSize == values.length)
      values = Arrays.copyOf(values, 2 * values.length);
   values[valuesSize] = in.nextDouble();
   valuesSize++;
}
Copying an Array Reference vs. Copying an Array

Figure 7.13. Copying an Array Reference vs. Copying an Array

Growing an Array

Figure 7.14. Growing an Array

Printing Element Separators

When you display the elements of an array or array list, you usually want to separate them, often with commas or vertical lines, like this:

Ann | Bob | Cindy

Note that there is one fewer separator than there are elements. Print the separator before each element except the initial one (with index 0):

for (int i = 0; i < names.size(); i++)
{
   if (i > 0)
   {
      System.out.print(" | ");
   }
   System.out.print(names.get(i));
}

The following sample program implements a Bank class that stores an array list of bank accounts. The methods of the Bank class use some of the algorithms that we have discussed in this section.

ch07/bank/Bank.java

1 import java.util.ArrayList;
2
3 /**
4    This bank contains a collection of bank accounts.
5 */
6 public class Bank
7 {
8    private ArrayList<BankAccount> accounts;
9
10    /**
11       Constructs a bank with no bank accounts.
12    */
13    public Bank()
14    {
15       accounts = new ArrayList<BankAccount>();
16    }
17
18    /**
19       Adds an account to this bank.
20       @param a the account to add
21    */
22    public void addAccount(BankAccount a)
23    {
24        accounts.add(a);
25    }
26
27    /**
28       Gets the sum of the balances of all accounts in this bank.
29       @return the sum of the balances
30    */
31    public double getTotalBalance()
32    {
33       double total = 0;
34       for (BankAccount a : accounts)
35       {
36           total = total + a.getBalance();
37       }
38       return total;
39    }
40
41    /**
42       Counts the number of bank accounts whose balance is at
43       least a given value.
44       @param atLeast the balance required to count an account
45       @return the number of accounts having at least the given balance
46    */
47    public int countBalancesAtLeast(double atLeast)
48    {
49       int matches = 0;
50       for (BankAccount a : accounts)
51       {
52         if (a.getBalance() >= atLeast) matches++; // Found a match
53       }
54       return matches;
55    }
56
57    /**
58         Finds a bank account with a given number.
59         @param accountNumber the number to find
60         @return the account with the given number, or null if there
61         is no such account
62    */
63    public BankAccount find(int accountNumber)
64    {
65         for (BankAccount a : accounts)
66         {
67            if (a.getAccountNumber() == accountNumber) // Found a match
68               return a;
69         }
70         return null; // No match in the entire array list
71    }
72
73    /**
74         Gets the bank account with the largest balance.
75         @return the account with the largest balance, or null if the
76         bank has no accounts
77    */
78    public BankAccount getMaximum()
79    {
80         if (accounts.size() == 0) return null;
81         BankAccount largestYet = accounts.get(0);
82         for (int i = 1; i < accounts.size(); i++)
83         {
84            BankAccount a = accounts.get(i);
85            if (a.getBalance() > largestYet.getBalance())
86               largestYet = a;
87         }
88         return largestYet;
89    }
90 }

ch07/bank/BankTester.java

1 /**
2    This program tests the Bank class.
3 */
4 public class BankTester
5 {
6    public static void main(String[] args)
7    {
8        Bank firstBankOfJava = new Bank();
9        firstBankOfJava.addAccount(new BankAccount(1001, 20000));
10        firstBankOfJava.addAccount(new BankAccount(1015, 10000));
11        firstBankOfJava.addAccount(new BankAccount(1729, 15000));
12
13        double threshold = 15000;
14        int count = firstBankOfJava.countBalancesAtLeast(threshold);
15        System.out.println("Count: " + count);
16        System.out.println("Expected: 2");
17
18        int accountNumber = 1015;
19        BankAccount account = firstBankOfJava.find(accountNumber);
20        if (account == null)
21          System.out.println("No matching account");
22        else
23           System.out.println("Balance of matching account: "
24              + account.getBalance());
25        System.out.println("Expected: 10000");
26
27        BankAccount max = firstBankOfJava.getMaximum();
28        System.out.println("Account with largest balance: "
29              + max.getAccountNumber());
30        System.out.println("Expected: 1001");
31    }
32 }

Program Run

Count: 2
Expected: 2
Balance of matching account: 10000.0
Expected: 10000
Account with largest balance: 1001
Expected: 1001
Printing Element Separators

13. Would it be possible to use a "for each" loop in the getMaximum method?

14. When printing separators, we skipped the separator before the initial element. Rewrite the loop so that the separator is printed after each element, except for the last element.

15. The following replacement has been suggested for the algorithm in Section 7.6.10.

System.out.print(names.get(0));
for (int i = 1; i < names.size(); i++) System.out.print(" | " + names.get(i));

What is problematic about this suggestion?

Regression Testing

It is a common and useful practice to make a new test whenever you find a program bug. You can use that test to verify that your bug fix really works. Don't throw the test away; feed it to the next version after that and all subsequent versions. Such a collection of test cases is called a test suite.

Note

A test suite is a set of tests for repeated testing.

You will be surprised how often a bug that you fixed will reappear in a future version. This is a phenomenon known as cycling. Sometimes you don't quite understand the reason for a bug and apply a quick fix that appears to work. Later, you apply a different quick fix that solves a second problem but makes the first problem appear again. Of course, it is always best to think through what really causes a bug and fix the root cause instead of doing a sequence of "Band-Aid" solutions. If you don't succeed in doing that, however, you at least want to have an honest appraisal of how well the program works. By keeping all old test cases around and testing them against every new version, you get that feedback. The process of checking each version of a program against a test suite is called regression testing.

Note

Regression testing involves repeating previously run tests to ensure that known failures of prior versions do not appear in new versions of the software.

How do you organize a suite of tests? An easy technique is to produce multiple tester classes, such as BankTester1, BankTester2, and so on.

Another useful approach is to provide a generic tester, and feed it inputs from multiple files. Consider this tester for the Bank class of Section 7.6:

ch07/regression/BankTester.java

1 import java.util.Scanner;
2
3 /**
4    This program tests the Bank class.
5 */
6 public class BankTester
7 {
8     public static void main(String[] args)
9     {
10        Bank firstBankOfJava = new Bank();
11        firstBankOfJava.addAccount(new BankAccount(1001, 20000));
12        firstBankOfJava.addAccount(new BankAccount(1015, 10000));
13        firstBankOfJava.addAccount(new BankAccount(1729, 15000));
14
15        Scanner in = new Scanner(System.in);
16
17        double threshold = in.nextDouble();
18        int c = firstBankOfJava.count(threshold);
19        System.out.println("Count: " + c);
20        int expectedCount = in.nextInt();
21        System.out.println("Expected: " + expectedCount);
22
23        int accountNumber = in.nextInt();
24        BankAccount a = firstBankOfJava.find(accountNumber);
25        if (a == null)
26          System.out.println("No matching account");
27        else
28        {
29          System.out.println("Balance of matching account: " + a.getBalance());
30          int matchingBalance = in.nextInt();
31          System.out.println("Expected: " + matchingBalance);
32       }
33    }
34 }

Rather than using fixed values for the threshold and the account number to be found, the program reads these values, and the expected responses. By running the program with different inputs, we can test different scenarios, such as the ones for diagnosing off-by-one errors discussed in Common Error 6.2.

Of course, it would be tedious to type in the input values by hand every time the test is executed. It is much better to save the inputs in a file, such as the following:

ch07/regression/input1.txt

15000
2
1015
10000

The command line interfaces of most operating systems provide a way to link a file to the input of a program, as if all the characters in the file had actually been typed by a user. Type the following command into a shell window:

java BankTester < input1.txt

The program is executed, but it no longer reads input from the keyboard. Instead, the System.in object (and the Scanner that reads from System.in) gets the input from the file input1.txt. This process is called input redirection.

The output is still displayed in the console window:

Program Run

Count: 2
Expected: 2
Balance of matching account: 10000
Expected: 10000

You can also redirect output. To capture the output of a program in a file, use the command

java BankTester < input1.txt > output1.txt

This is useful for archiving test cases.

Regression Testing

17. Suppose a customer of your program finds an error. What action should you take beyond fixing the error?

18. Why doesn't the BankTester program contain prompts for the inputs?

Two-Dimensional Arrays

Arrays and array lists can store linear sequences. Occasionally you want to store collections that have a two-dimensional layout. The traditional example is the tictac-toe board (see Figure 15).

Such an arrangement, consisting of rows and columns of values, is called a two-dimensional array or matrix. When constructing a two-dimensional array, you specify how many rows and columns you need. In this case, ask for 3 rows and 3 columns:

final int ROWS = 3;
final int COLUMNS = 3;
String[][] board = new String[ROWS][COLUMNS];

Note

Two-dimensional arrays form a tabular, two-dimensional arrangement. You access elements with an index pair a[i][j].

This yields a two-dimensional array with 9 elements

board[0][0]   board[0][1]    board[0][2]
board[1][0]   board[1][1]    board[1][2]
board[2][0]   board[2][1]    board[2][2]

To access a particular element, specify two index values in separate brackets. For example:

board[1][1] = "x";
board[2][1] = "o";

When filling or searching a two-dimensional array, it is common to use two nested loops. For example, this pair of loops sets all elements in the array to spaces.

for (int i = 0; i < ROWS; i++)
   for (int j = 0; j < COLUMNS; j++)
      board[i][j] = " ";
A Tic-Tac-Toe Board

Figure 7.15. A Tic-Tac-Toe Board

In this loop, we used constants for the number of rows and columns. You can also recover the array dimensions from the array variable:

  • board.length is the number of rows.

  • board[0].length is the number of columns. (See Special Topic 7.3 on page 313 for an explanation of this expression.)

You can rewrite the loop for filling the tic-tac-toe board as

for (int i = 0; i < board.length; i++)
   for (int j = 0; j < board[0].length; j++)
      board[i][j] = " ";

Here is a class and a test program for playing tic-tac-toe. This class does not check whether a player has won the game. That is left as an exercise—see Exercise P7.13.

ch07/twodim/TicTacToe.java

1 /**
2    A 3 x 3 tic-tac-toe board.
3 */
4 public class TicTacToe
5 {
6    private String[][] board;
7    private static final int ROWS = 3;
8    private static final int COLUMNS = 3;
9
10    /**
11       Constructs an empty board.
12    */
13    public TicTacToe()
14    {
15       board = new String[ROWS][COLUMNS];
16       // Fill with spaces
17       for (int i = 0; i < ROWS; i++)
18          for (int j = 0; j < COLUMNS; j++)
19              board[i][j] = " ";
20    }
21
22    /**
23       Sets a field in the board. The field must be unoccupied.
24       @param i the row index
25       @param j the column index
26       @param player the player ("x" or "o")
27    */
28    public void set(int i, int j, String player)
29    {
30       if (board[i][j].equals(" "))
31           board[i][j] = player;
32    }
33
34    /**
35       Creates a string representation of the board, such as
36       |x o|
37       | x |
38       |  o|.
39       @return the string representation
40    */
41    public String toString()
42    {
43       String r = "";
44       for (int i = 0; i < ROWS; i++)
45       {
46          r = r + "|";
47          for (int j = 0; j < COLUMNS; j++)
48             r = r + board[i][j];
49          r = r + "|
";
50       }
51       return r;
52    }
53 }

ch07/twodim/TicTacToeRunner.java

1 import java.util.Scanner;
2
3 /**
4    This program runs a TicTacToe game. It prompts the
5    user to set positions on the board and prints out the
6    result.
7 */
8 public class TicTacToeRunner
9 {
10    public static void main(String[] args)
11     {
12       Scanner in = new Scanner(System.in);
13       String player = "x";
14       TicTacToe game = new TicTacToe();
15       boolean done = false;
16       while (!done)
17       {
18          System.out.print(game.toString());
19          System.out.print(
20                "Row for " + player + " (-1 to exit): ");
21          int row = in.nextInt();
22          if (row < 0) done = true;
23          else
24          {
25             System.out.print("Column for " + player + ": ");
26             int column = in.nextInt();
27             game.set(row, column, player);
28             if (player.equals("x"))
29                player = "o";
30             else
31                player = "x";
32           }
33        }
34     }
35 }

Program Run

|   |
|   |
|   |
Row for x (-1 to exit): 1
Column for x: 2
|   |
|  x|
|   |
Row for o (-1 to exit): 0
Column for o: 0
|o  |
|  x|
|   |
Row for x (−1 to exit): −1
A Tic-Tac-Toe Board

20. How do you count the number of spaces in the tic-tac-toe board?

Summary of Learning Objectives

Use arrays for collecting values.

  • An array is a sequence of values of the same type.

  • You access an array element with an integer index, using the [] operator.

  • Index values of an array range from 0 to length - 1.

  • Accessing a nonexistent element results in a bounds error.

  • The expression array.length yields the number of elements in an array.

  • Avoid parallel arrays by changing them into arrays of objects.

Use array lists for managing collections whose size can change.

  • The ArrayList class manages a sequence of objects whose size can change.

  • The ArrayList class is a generic class: ArrayList<TypeName> collects objects of the given type.

Use wrapper classes when working with array lists of numbers.

  • To treat primitive type values as objects, you must use wrapper classes.

Use the enhanced for loop to visit all elements of a collection.

  • The enhanced for loop traverses all elements of a collection.

  • In an enhanced for loop, the loop variable contains an element, not an index.

Work with arrays that are partially filled.

  • With a partially filled array, keep a companion variable to track how many elements are used.

Be able to use common array algorithms.

  • To count values, check all elements and count the matches until you reach the end.

  • To compute the maximum or minimum value, initialize a candidate with the starting element. Then compare the candidate with the remaining elements and update it if you find a larger or smaller value.

  • To find a value, check all elements until you have found a match.

  • An array variable stores a reference to the array. Copying the variable yields a second reference to the same array.

  • Use the Arrays.copyOf method to copy the elements of an array.

Describe the process of regression testing.

  • A test suite is a set of tests for repeated testing.

  • Regression testing involves repeating previously run tests to ensure that known failures of prior versions do not appear in new versions of the software.

Use two-dimensional arrays for data that is arranged in rows and columns.

  • Two-dimensional arrays form a tabular, two-dimensional arrangement. You access elements with an index pair a[i][j].

Classes, Objects, and Methods Introduced in this Chapter

java.lang.Boolean                           java.util.ArrayList<E>
   booleanValue                                add
java.lang.Double                               get
   doubleValue                                 remove
java.lang.Integer                              set
    intValue                                   size
java.util.Arrays
   copyOf
   toString

Media Resources

  • Worked Example Rolling the Dice

  • Worked Example A World Population Table

  • • Lab Exercises

  • Media Resources
  • Media Resources
  • Media Resources
  • Media Resources

Review Exercises

R7.1 What is an index? What are the bounds of an array or array list? What is a bounds error?

R7.2 Write a program that contains a bounds error. Run the program. What happens on your computer? How does the error message help you locate the error?

R7.3 Write Java code for a loop that simultaneously computes the maximum and minimum values of an array list. Use an array list of accounts as an example.

R7.4 Write a loop that reads 10 strings and inserts them into an array list. Write a second loop that prints out the strings in the opposite order from which they were entered.

R7.5 Consider the algorithm that we used for determining the maximum value in an array list. We set largestYet to the starting element, which meant that we were no longer able to use the "for each" loop. An alternate approach is to initialize largestYet with null, then loop through all elements. Of course, inside the loop you need to test whether largestYet is still null. Modify the loop that finds the bank account with the largest balance, using this technique. Is this approach more or less efficient than the one used in the text?

R7.6 Consider another variation of the algorithm for determining the maximum value. Here, we compute the maximum value of an array of numbers.

double max = 0; // Contains an error!
for (double element : values)
{
   if (element > max) max = element;
}

However, this approach contains a subtle error. What is the error, and how can you fix it?

R7.7 For each of the following sets of values, write code that fills an array a with the values.

  1. 1 2 3 4 5 6 7 8 9 10

  2. 0 2 4 6 8 10 12 14 16 18 20

  3. 1 4 9 16 25 36 49 64 81 100

  4. 0 0 0 0 0 0 0 0 0 0

  5. 1 4 9 16 9 7 4 9 11

Use a loop when appropriate.

R7.8 Write a loop that fills an array a with 10 random numbers between 1 and 100. Write code (using one or more loops) to fill a with 10 different random numbers between 1 and 100.

R7.9 What is wrong with the following loop?

double[] values = new double[10];
for (int i = 1; i <= 10; i++) values[i] = i * i;

Explain two ways of fixing the error.

R7.10 Write a program that constructs an array of 20 integers and fills the first ten elements with the numbers 1, 4, 9, . . ., 100. Compile it and launch the debugger. After the array has been filled with three numbers, inspect it. What are the contents of the elements in the array beyond those that you filled?

R7.11 Rewrite the following loops without using the "for each" construct. Here, values has type double.

  1. for (double element : values) sum = sum + element;

  2. for (double element : values) if (element == target) return true;

  3. int i = 0;
    for (double element : values) { values[i] = 2 * element; i++; }

R7.12 Rewrite the following loops, using the "for each" construct. Here, values has type double.

  1. for (int i = 0; i < values.length; i++) sum = sum + values[i];

  2. for (int i = 1; i < values.length; i++) sum = sum + values[i];

  3. for (int i = 0; i < values.length; i++)
       if (values[i] == target) return i;

R7.13 What is wrong with these statements for printing an array list with separators?

System.out.print(values.get(0));
for (int i = 1; i < values.size(); i++)
{
   System.out.print(", " + values.get(i));
}

R7.14 When finding the position of a match in Section 7.6.6, we used a while loop, not a for loop. What is wrong with using this loop instead?

for (pos = 0; pos < values.size() && !found; pos++)
{
   if (values.get(pos) > 100)
   {
      found = true;
   }
}

R7.15 When inserting an element into an array in Section 7.6.8, we moved the elements with larger index values, starting at the end of the array. Why is it wrong to start at the insertion location, like this?

for (int i = pos; i < size - 1; i++)
{
   values[i + 1] = values[i];
}

R7.16 In Section 7.6.9, we doubled the length of the array when growing it. Why didn't we just increase the size by one element?

R7.17 What are parallel arrays? Why are parallel arrays indications of poor programming? How can they be avoided?

R7.18 True or false?

  1. All elements of an array are of the same type.

  2. An array index must be an integer.

  3. Arrays cannot contain string references as elements.

  4. Arrays cannot contain null references as elements.

  5. Parallel arrays must have equal length.

  6. Two-dimensional arrays always have the same numbers of rows and columns.

  7. Two parallel arrays can be replaced by a two-dimensional array.

  8. Elements of different columns in a two-dimensional array can have different types.

R7.19 Define the terms regression testing and test suite.

R7.20 What is the debugging phenomenon known as cycling? What can you do to avoid it?

Programming Exercises

P7.1 Implement a class Purse. A purse contains a collection of coins. For simplicity, we will only store the coin names in an ArrayList<String>. (We will discuss a better representation in Chapter 8.) Supply a method

void addCoin(String coinName)

Add a method toString to the Purse class that prints the coins in the purse in the format

Purse[Quarter,Dime,Nickel,Dime]

P7.2 Write a method reverse that reverses the sequence of coins in a purse. Use the toString method of the preceding assignment to test your code. For example, if reverse is called with a purse

Purse[Quarter,Dime,Nickel,Dime]

then the purse is changed to

Purse[Dime,Nickel,Dime,Quarter]

P7.3 Add a method to the Purse class

public void transfer(Purse other)

that transfers the contents of one purse to another. For example, if a is

Purse[Quarter,Dime,Nickel,Dime]

and b is

Purse[Dime,Nickel]

then after the call a.transfer(b), a is

Purse[Quarter,Dime,Nickel,Dime,Dime,Nickel]

and b is empty.

P7.4 Write a method for the Purse class

public boolean sameContents(Purse other)

that checks whether the other purse has the same coins in the same order.

P7.5 Write a method for the Purse class

public boolean sameCoins(Purse other)

that checks whether the other purse has the same coins, perhaps in a different order. For example, the purses

Purse[Quarter,Dime,Nickel,Dime]

and

Purse[Nickel,Dime,Dime,Quarter]

should be considered equal.

You will probably need one or more helper methods.

P7.6 A Polygon is a closed curve made up from line segments that join the polygon's corner points. Implement a class Polygon with methods

public double perimeter()

and

public double area()

that compute the circumference and area of a polygon. To compute the perimeter, compute the distance between adjacent points, and total up the distances. The area of a polygon with corners (x0, y0), . . ., (xn−1, yn−1) is

Programming Exercises

As test cases, compute the perimeter and area of a rectangle and of a regular hexagon. Note: You need not draw the polygon –– that is done in Exercise P7.18.

P7.7 Write a program that reads a sequence of integers into an array and that computes the alternating sum of all elements in the array. For example, if the program is executed with the input data

1 4 9 16 9 7 4 9 11

then it computes

1 − 4 − 9 − 16 − 9 − 7 − 4 − 9 − 11 = −2

P7.8 Write a program that produces random permutations of the numbers 1 to 10. To generate a random permutation, you need to fill an array with the numbers 1 to 10 so that no two entries of the array have the same contents. You could do it by brute force, by calling Random.nextInt until it produces a value that is not yet in the array. Instead, you should implement a smart method. Make a second array and fill it with the numbers 1 to 10. Then pick one of those at random, remove it, and append it to the permutation array. Repeat 10 times. Implement a class PermutationGenerator with a method

int[] nextPermutation

P7.9 A run is a sequence of adjacent repeated values. Write a program that generates a sequence of 20 random die tosses and that prints the die values, marking the runs by including them in parentheses, like this:

1 2 (5 5) 3 1 2 4 3 (2 2 2 2) 3 6 (5 5) 6 3 1

Use the following pseudocode:

Set a boolean variable inRun to false.
For each valid index i in the array list
   If inRun
      If values[i] is different from the preceding value
         Print )
         inRun = false
   Else
      If values[i] is the same as the following value
         Print (
         inRun = true
   Print values[i]
If inRun, print )

P7.10 Write a program that generates a sequence of 20 random die tosses and that prints the die values, marking only the longest run, like this:

1 2 5 5 3 1 2 4 3 (2 2 2 2) 3 6 5 5 6 3 1

If there is more than one run of maximum length, mark the first one.

P7.11 It is a well-researched fact that men in a restroom generally prefer to maximize their distance from already occupied stalls, by occupying the middle of the longest sequence of unoccupied places.

For example, consider the situation where ten stalls are empty.

_ _ _ _ _ _ _ _ _ _

The first visitor will occupy a middle position:

_ _ _ _ _ X _ _ _ _

The next visitor will be in the middle of the empty area at the left.

_ _ X _ _ X _ _ _ _

Write a program that reads the number of stalls and then prints out diagrams in the format given above when the stalls become filled, one at a time. Hint: Use an array of boolean values to indicate whether a stall is occupied.

P7.12 In this assignment, you will model the game of Bulgarian Solitaire. The game starts with 45 cards. (They need not be playing cards. Unmarked index cards work just as well.) Randomly divide them into some number of piles of random size. For example, you might start with piles of size 20, 5, 1, 9, and 10. In each round, you take one card from each pile, forming a new pile with these cards. For example, the sample starting configuration would be transformed into piles of size 19, 4, 8, 10, and 5. The solitaire is over when the piles have size 1, 2, 3, 4, 5, 6, 7, 8, and 9, in some order. (It can be shown that you always end up with such a configuration.)

In your program, produce a random starting configuration and print it. Then keep applying the solitaire step and print the result. Stop when the solitaire final configu-ration is reached.

P7.13 Add a method getWinner to the TicTacToe class of Section 7.8. It should return "x" or "o" to indicate a winner, or " " if there is no winner yet. Recall that a winning position has three matching marks in a row, column, or diagonal.

P7.14 Write an application that plays tic-tac-toe. Your program should draw the game board, change players after every successful move, and pronounce the winner.

P7.15 Magic squares. An n × n matrix that is filled with the numbers 1, 2, 3, . . ., n2 is a magic square if the sum of the elements in each row, in each column, and in the two diagonals is the same value. For example,

Programming Exercises

Write a program that reads in n2 values from the keyboard and tests whether they form a magic square when arranged as a square matrix. You need to test three features:

  • Did the user enter n2 numbers for some n?

  • Do each of the numbers 1, 2, . . ., n2 occur exactly once in the user input?

  • When the numbers are put into a square, are the sums of the rows, columns, and diagonals equal to each other?

If the size of the input is a square, test whether all numbers between 1 and n2 are present. Then compute the row, column, and diagonal sums. Implement a class Square with methods

public void add(int i)
public boolean isMagic()

P7.16 Implement the following algorithm to construct magic n-by-n2 squares; it works only if n is odd. Place a 1 in the middle of the bottom row. After k has been placed in the (i, j) square, place k + 1 into the square to the right and down, wrapping around the borders. However, if the square to the right and down has already been filled, or if you are in the lower-right corner, then you must move to the square straight up instead. Here is the 5 × 5 square that you get if you follow this method:

Programming Exercises

Write a program whose input is the number n and whose output is the magic square of order n if n is odd. Implement a class MagicSquare with a constructor that constructs the square and a toString method that returns a representation of the square.

P7.17 Implement a class Cloud that contains an array list of Point2D.Double objects. Support methods

public void add(Point2D.Double aPoint)
public void draw(Graphics2D g2)

Draw each point as a tiny circle.

Write a graphical application that draws a cloud of 100 random points.

P7.18 Implement a class Polygon that contains an array list of Point2D.Double objects. Support methods

public void add(Point2D.Double aPoint)
public void draw(Graphics2D g2)

Draw the polygon by joining adjacent points with a line, and then closing it up by joining the end and start points.

Write a graphical application that draws a square and a pentagon using two Polygon objects.

P7.19 Write a class Chart with methods

public void add(int value)
public void draw(Graphics2D g2)

that displays a stick chart of the added values, like this:

Programming Exercises

You may assume that the values are pixel positions.

P7.20 Write a class BarChart with methods

public void add(double value)
public void draw(Graphics2D g2)

that displays a chart of the added values. You may assume that all added values are positive. Stretch the bars so that they fill the entire area of the screen. You must figure out the maximum of the values, and then scale each bar.

P7.21 Improve the BarChart class of Exercise P7.20 to work correctly when the data contains negative values.

P7.22 Write a class PieChart with methods

public void add(double value)
public void draw(Graphics2D g2)

that displays a pie chart of the added values. You may assume that all data values are positive.

Programming Projects

Project 7.1 Poker Simulator. In this assignment, you will implement a simulation of a popular casino game usually called video poker. The card deck contains 52 cards, 13 of each suit. At the beginning of the game, the deck is shuffled. You need to devise a fair method for shuffling. (It does not have to be efficient.) Then the top five cards of the deck are presented to the player. The player can reject none, some, or all of the cards. The rejected cards are replaced from the top of the deck. Now the hand is scored. Your program should pronounce it to be one of the following:

  • No pair—The lowest hand, containing five separate cards that do not match up to create any of the hands below.

  • One pair—Two cards of the same value, for example two queens.

  • Two pairs—Two pairs, for example two queens and two 5's.

  • Three of a kind—Three cards of the same value, for example three queens.

  • Straight—Five cards with consecutive values, not necessarily of the same suit, such as 4, 5, 6, 7, and 8. The ace can either precede a 2 or follow a king.

  • Flush—Five cards, not necessarily in order, of the same suit.

  • Full House—Three of a kind and a pair, for example three queens and two 5's

  • Four of a Kind—Four cards of the same value, such as four queens.

  • Straight Flush—A straight and a flush: Five cards with consecutive values of the same suit.

  • Royal Flush—The best possible hand in poker. A 10, jack, queen, king, and ace, all of the same suit.

If you are so inclined, you can implement a wager. The player pays a JavaDollar for each game, and wins according to the following payout chart:

Hand

Payout

Hand

Payout

Royal Flush

250

Straight

4

Straight Flush

50

Three of a Kind

3

Four of a Kind

25

Two Pair

2

Full House

6

Pair of Jacks or Better

1

Flush

5

  

Project 7.2 The Game of Life is a well-known mathematical game that gives rise to amazingly complex behavior, although it can be specified by a few simple rules. (It is not actually a game in the traditional sense, with players competing for a win.) Here are the rules. The game is played on a rectangular board. Each square can be either empty or occupied. At the beginning, you can specify empty and occupied cells in some way; then the game runs automatically. In each generation, the next generation is computed. A new cell is born on an empty square if it is surrounded by exactly three occupied neighbor cells. A cell dies of overcrowding if it is surrounded by four or more neighbors, and it dies of loneliness if it is surrounded by zero or one neighbor. A neighbor is an occupant of an adjacent square to the left, right, top, or bottom or in a diagonal direction. Figure 16 shows a cell and its neighbor cells.

Many configurations show interesting behavior when subjected to these rules. Figure 17 shows a glider, observed over five generations. Note how it moves. After four generations, it is transformed into the identical shape, but located one square to the right and below.

One of the more amazing configurations is the glider gun: a complex collection of cells that, after 30 moves, turns back into itself and a glider (see Figure 18).

Program the game to eliminate the drudgery of computing successive generations by hand. Use a two-dimensional array to store the rectangular configuration. Write a program that shows successive generations of the game. You may get extra credit if you implement a graphical application that allows the user to add or remove cells by clicking with the mouse.

Neighborhood of a Cell

Figure 7.16. Neighborhood of a Cell

Glider

Figure 7.17. Glider

Glider Gun

Figure 7.18. Glider Gun

Answers to Self-Check Questions

  1. 0, 1, 4, 9, 16, 25, 36, 49, 64, 81, but not 100.

  2. (a) 0; (b) a run-time error: array index out of bounds; (c) a compile-time error: c is not initialized.

  3. new String[10];
    new ArrayList<String>();
  4. names contains the strings "B" and "C" at positions 0 and 1.

  5. double is one of the eight primitive types. Double is a class type.

  6. values.set(0, values.get(0) + 1);

  7. for (double element : values) System.out.println(element);

  8. It counts how many accounts have a zero balance.

  9. for (int i = valuesSize - 1; i >= 0; i--) System.out.println(values[i]);

  10. valuesSize--;

  11. You need to use wrapper objects in an ArrayList<Double>, which is less efficient.

  12. It returns the first match that it finds.

  13. Yes, but the first comparison would always fail.

  14. for (int i = 0; i < values.size(); i++)
    {
       System.out.print(values.get(i));
       if (i < values.size() - 1)
        {
          System.out.print(" | ");
        }
    }

    Now you know why we set up the loop the other way.

  15. If names happens to be empty, the first line causes a bounds error.

  16. It is possible to introduce errors when modifying code.

  17. Add a test case to the test suite that verifies that the error is fixed.

  18. There is no human user who would see the prompts because input is provided from a file.

  19. int[][] array = new int[4][4];

  20. int count = 0;
    for (int i = 0; i < ROWS; i++)
       for (int j = 0; j < COLUMNS; j++)
          if (board[i][j].equals(" ")) count++;
..................Content has been hidden....................

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