Mini-Lab: More Fish!

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.



Becoming a Collector

Introduction

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.

Exercise

  • Replace the three fish variables in the AquaSimApplication class with a single variable that will represent the list of fish in the aquarium. Give it a name that indicates its meaning or purpose in the program. Initialize your new variable to refer to an initially-empty list.
  • Don't forget to import the appropriate library class (java.util.ArrayList).
  • Test your changes.

  • Stop and Think

    Do you expect your program to compile or to work? If not, why not? What do you need to do to test the changes you have made so far?

    It is always difficult to test modified code part-way through a change. At this point, you have constructed a collection that could contain three (or more) fish in the future, but does not contain any yet. The fish that you had before no longer exist. To test that you have not introduced any syntax errors when constructing and initializing your new variable, you want the program with those new changes to compile. To do this, you will have to "comment out" the code in the main method that no longer works. You will replace this code in the next few exercises, but it is easier to replace it when you can see what the old code was, which is why it is better to comment out the code rather than deleting it until you have implemented the replacement code. Don't be surprised, though, when your program no longer moves or displays fish!



Going Fishing and Viewing the Catch

Introduction

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.

Exercise

  • Construct three new fish and add them to your ArrayList. Don't forget to pass the aquarium and a color to the AquaFish constructor.
  • Add each fish to the aquarium. Assuming that you no longer have a variable defined for each fish, you will have to refer to them as indexed items in the list. You may leave the code for moving the fish commented out for now.
  • Test your program to make sure that your program is once again correctly constructing and displaying three fish.



Loopy Fish

Introduction

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.

Exercise

  • Replace your code from the last exercise (where you constructed each fish and added it to the aquarium individually) with a loop to construct and add the three fish. Test your program to make sure that its behavior is unchanged.
  • Now that your program will allow you to create multiple fish easily, it's time to let the user decide how many fish there should be. Read the class documentation for the AquaSimGUI class, to learn how to use its three-parameter constructor to prompt the user for the number of fish. Modify your program to use this constructor, updating the internal documentation as appropriate.

  • Modify the loop your created to construct the fish, so that it uses the number of fish specified by the user. (Stop and Think: Does the order in which you are constructing the aquarium, the fish in the aquarium, and the graphical user interface matter? Do you need to change the order in which they are constructed?)
  • Test your program to make sure that it correctly constructs and displays the number of fish specified by the user.



A School In Motion

Introduction

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:

Processing all the fish and all the steps in the simulation are not independent of one another. That is, we can't process all the fish and then process all the simulation steps, or vice versa. Either processing all the fish is part of what we do in one step of the simulation, or running all the steps of the simulation is part of what we do for each fish. Thus, we will need to nest one of the loops inside the other.

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

  • Remove the remaining comments around the code that runs through the steps of the simulation, moving and displaying the fish in the aquarium.
  • Inside the simulation loop, replace the code that moves the three named fish with a loop that will move all the fish in your collection. (Each will still change direction when it has to or when it randomly chooses to.) (Stop and Think: Consider the two types of loops for accessing all items in a collection: the for each statement and the traditional for statement. Do you have a choice of which to use, or must you use the traditional for statement?)
  • Test your modifications.
  • Update your internal documentation (regular comments) and your class documentation (class and method javadoc comments) to reflect any changes in the behavior of the code, the class, the methods, or the program as a whole.