Using ArrayLists
This set of Mini-Lab Exercises is the fourth in a series in which students build a small program with several fish moving around in an aquarium. The set includes the following exercises:
Each section contains an Introduction to a problem or task, descriptions or examples of one or more Concepts to apply in solving the problem or completing the task, and an Exercise.
In the exercises that precede this one, students will have created three
fish that move randomly back and forth in an aquarium,
being careful not to hit the sides. Students should be familiar with basic
for loops, simple selection statements, prompting for input, and the
java.util.Random
class.
A more realistic simulation of an aquarium would have more than two or three fish. We could modify the simulation so that it supports four fish, five fish, or twelve fish in that many variables, but if we "hard-code" the number of fish into the program in this way, then we must modify and re-compile the program to change the number of fish in the aquarium. We must also repeat statements, such as the code to move fish, four, five, or twelve times.
A better alternative would be to ask the user how many fish to place
in the aquarium and then store them in a collection object. We can use
a Java ArrayList
(from the java.util
package) to store the
collection of fish.
Java provides a number of classes that provide different ways to keep
track of a group or collection of objects. The Java
ArrayList
class is one of these.
An ArrayList
object
is a list of items with several important characteristics:
ArrayList
without knowing in
advance how many items the list will eventually hold. The syntax for creating a Java ArrayList
is:
ArrayList<Type> v; // v is a potential reference to an ArrayList
v = new ArrayList<Type>(); // v now refers to a newly constructed ArrayList
where the <Type
> portion indicates what
type of object the ArrayList
will hold. For example, the
following code fragment creates an empty deck of cards:
ArrayList<Card> deck = new ArrayList<Card>();
Exercise
|
Of course, we don't see any fish, because we haven't created any. But we do have a container to hold them. When we create fish, we can put them directly into the collection by adding them to the list. Once we have fish in the collection, how do we do anything with them? We can use Indexed Random Access to refer to any individual item in a list.
The add
method in the ArrayList
class
adds a new item to the end of the list.
For example, if deck
is a Java ArrayList
of playing
cards, then either of the following code fragments adds a new
Card
to the end of the deck:
Adding an object using a variable | Adding an object without a variable |
---|---|
|
|
Using the style in the second example, we do not need to create 52 different variables to add 52 cards to the deck.
To access a specific item in a list directly, we specify its index.
List indices start at 0, not at 1, so if
there are N items in a list, the valid indices are 0 to N-1.
The expression deck.size()
indicates how many items are
in the list (the N for that list).
For example, if deck
is a Java ArrayList
of playing
cards, then we could use the following statements to display
various cards in the deck.
Card to display | Code to display it |
---|---|
first card in the deck |
|
sixth card in the deck |
(assuming that there are at least 6 cards in the deck)
|
last card in the deck |
|
Indices can also be used to modify an element in a data structure. For example, to swap the 1st and 52nd cards in the deck, the following code might be used:
Card temp = deck.get(0); // temp refers to 1st card
deck.set(0, deck.get(51)); // 1st card is now same as 52nd card
deck.set(51, temp); // 52nd card is now old 1st card
Exercise
|
One of the reasons we switched to using a collection object
was to avoid having to duplicate code for each fish, but we currently have three
lines to add the three fish to the aquarium, and we would need to repeat
code to move the fish also. This will not scale up well if we want
to put many fish in the aquarium! We can access all of the fish
sequentially using either of two different for
loop
variations.
There are two common Java idioms to access all the items in a
collection using a for
statement.
The simpler approach,
if all you want to do is to access every item in the list,
is a special-purpose for
statement often known as a for each loop.
The structure of a for each statement
for stepping through a container of items
is shown below. <Type>
should be replaced with the
type of item in the container; the <variable>
refers
to a different item from the container each time through the loop.
for ( <Type> <variable> : <container> )
{
<repeated action>
}
For example, the code fragment in Example 1 below displays all the cards in the deck from our examples above. This could be read as "For each Card instance in the deck (called cardInDeck), display the card."
Limitations of the for each structure: A for each statement can only be used when all the elements in the list are to be accessed and each element's position in the ArrayList doesn't matter. If you want to add items to or delete items from the list, replace the items in the list, or determine where in the list an object is located, then you cannot use a for each statment.
A common alternative is to use a for
statement that is
essentially a counted repetition, using the size of the collection
as N, as in the following examples. In both cases, the loop
counts from 0 to N-1, since the valid indices are 0 to N-1.
In fact, these two examples are
equivalent, except that Example 2 creates a named reference
(cardInDeck
) to the indexed item in the list (similar
to the for each loop).
This generally makes
subsequent actions using the indexed item easier to read, especially
when there are multiple statements in the body of the loop
referencing the indexed item.
Example 1: For each loop | Example 2: For loop | Example 3: For loop using a variable for readability |
---|---|---|
|
|
|
Examples 2 or 3 can be used to change which items are in the
collection. For example, in the following code fragment,
hand
is an ArrayList
of type
Card
, as is deck
. The code fragment
uses the set
method in the ArrayList
class
to replace each existing card in the hand with a new card
obtained from the deck.
WARNING: Be sure to use the standard idiomfor (int i = 0; i < hand.size(); i++)
hand.set(i, deck.dealACard();
(int i
= 0; i < collection.size(); i++)
to avoid
off-by-one errors.
Exercise
|
Now let's think about how to move and display all the fish for as many time steps as the user wants.
It seems clear that we will want to loop through the steps in the simulation, as we are already doing. It also seems clear that we will want to loop through all the fish. The question is:
To decide which loop gets nested inside of which, consider the following algorithmic structures in which we assume that we have 25 fish in the aquarium and we want to run the simulation 100 times.
For each fish in the collection:
Move 100 times and display the aquarium. |
For each step in the simulation:
Move the 25 fish once and display the aquarium. |
We do not want the first fish to move 100 times, followed by the second fish moving 100 times, followed by the third fish moving 100 times. Instead, we want all 25 fish to move once, then all 25 fish to move again. This corresponds to the second solution above.
Another question we have to consider is where the display of the aquarium should be relative to the fish movement. Do we want to display the aquarium in the outer loop (as part of each simulation step), or in the inner loop (as part of processing each fish), or in neither loop? The following table illustrates these three options.
For each step in the simulation:
For each fish in the collection: Move, possibly changing direction. Display aquarium & fish. |
For each step in the simulation:
For each fish in the collection: Move, possibly changing direction. Display aquarium & fish. |
For each step in the simulation:
For each fish in the collection: Move, possibly changing direction. Display aquarium & fish. |
Which behavior do you wish to implement?
Exercise
|