21. Bouncing Balls, Lists, and Loops

In these notes you will learn:

  • How to define ArrayLists.
  • How add objects to an ArrayList.
  • Hot to use for-loops to process each element of an ArrayList.
  • How to store integers and floating point numbers in ArrayList.

21.1. Introduction

We saw in the previous notes how to use objects to make three balls bounce around the screen, but we ran into a problem when we wanted to scale the program up. Placing some of our code inside objects made our program easier to conceptualize and run, but if want more than a few balls, say 100, to bounce around the screen, we have to write a lot of code and keep track of a lot of information. For example, we would need a new name for each ball that we make. Writing programs like that is a tedious and highly error prone process, so in these notes we’ll discuss using collection objects as a way to deal with this problem. In particular, we will use an ArrayList

21.2. Declaring ArrayList Variables

To handle multiple Ball objects at the same time, we’ll store them in a collection object known as an ArrayList:

ArrayList<Ball> ballList;

This statement creates a variable called ballList that can be used to refer to an ArrayList which contains Ball objects.

Note

As with other non-primitive objects, we have not yet created an ArrayList object — we have only declared the variable that can be used to refer to it.

In general, declaring an ArrayList involves the following:

  1. As with any object, we start by typing in ArrayList
  2. This is followed by < and then the type of objects we want the ``ArrayList`` hold. We end this part with a >, so that, for an object of type T we have so far put down: ArrayList<T>. In this context the symbols < > are referred to as angle-brackets
  3. Lastly, as with any object, comes the name we wish to give the ArrayList we’ve just created.

As another example, you can create an ArrayList variable to hold, say, a list of names:

ArrayList<String> names;

Here, names is an ArrayList variable that can refer to an ArrayList of String objects

By default, object variables (like ballList) are initialized to the special value null. A null value indicates that the variables does not yet point to any object. Sometimes when you run a program you get a null pointer exception error, which usually means that you’ve used a null variable as if it were instead ponting to an object.

21.3. Creating ArrayList Objects

To actually create an ArrayList object, we use the new keyword:

ArrayList<Ball> ballList; // ballList is null initially

void setup() {
    // ...

    ballList = new ArrayList<Ball>();
}

Note that after each time we write ArrayList we add Ball in angle-brackets < >, however, after the second ArrayList<Ball> we added parentheses. This is because we are calling the ArrayList constructor.

Here’s how you could initialize an ArrayList of Strings:

ArrayList<String> names:  // names is null initially

void setup() {
    // ...

    names = new ArrayList<String>();
}

21.4. Adding Elements to an ArrayList

To add elements to an ArrayList, the add function is used. It appends elements at the end of the list:

ArrayList<Ball> ballList; // create an initially null variable.

ballList = new ArrayList<Ball>(); // create an initially empty
                                  // ArrayList of ball objects

ballList.add(new Ball());
ballList.add(new Ball());
ballList.add(new Ball());

This results in ballList containing three Ball() objects, at positions 0, 1, and 2.

In general, each object stored in an ArrayList has a position. The first element has position 0, the second has position 1, and so on. If n is total number of elements, then the last element is at position n-1. Later we will see how to get n.

You use the names ArrayList to store names as strings:

ArrayList<String> names;

names = new ArrayList<String>();

names.add("James Hetfield");
names.add("Lars Ulrich");
names.add("Kirk Hammett");
names.add("Rob Trujillo");

ArrayLists can contain as may objects as you wish to add to them (within the limits of you computer’s memory, of course). They start out empty and expand in size as needed. This is a very useful feature, as it is often the case that you don’t know in advance how many items you will need to store. For example, if you are reading a file of names, you may not know ahead of time how many names are in the file. With an ArrayList, you don’t need to. You simply add each file as you get it.

21.5. Getting elements from an ArrayList

To retrieve an element from an array list we use the get(i) function, which returns the element stored at position i in the ArrayList:

ArrayList<String> names; // create an initially null variable.

ballList = new ArrayList<String>(); // create an initially empty
                                    // ArrayList of names objects

ballList.add("James Hetfield");
ballList.add("Lars Ulrich");
ballList.add("Kirk Hammett");
ballList.add("Rob Trujillo");

String lead_guitar = ballList.get(2);

This results in lead_guitar containing the String “Kirk Hammett”. However, you don’t want to use get to access objects en masse in an ArrayList For that we turn to the next section.

21.6. Processing an ArrayList with a for-each Loop

Now that we know how to create ArrayList and add object to them, let’s see how to process them. The simplest way to process an ArrayList is to use a for-each loop to run the same block of code on every object within it:

for (Ball b : balList) { // for-each loop
    b.render();
    b.dy += gravity;
}

Inside a loop like this, the : stands for the word “in”, adn so the statements for(Ball b : ballList) reads roughly as “for each ball b in ballList, do the following”.

The variable b declared inside the header of the for each loop is set to refer to each object of ballList, one object at a time. After b is set the code in the body of the loop is called.

Here’s another example with names:

for (String s : names) {
    println(s);
}

This prints every name in the names ArrayList on the screen (in the console!), one name at a time.

21.7. Multiple Bouncing Balls

We now have enough information to write a program that displays some bouncing balls. Every time you click the mouse, a new ball is added:

ArrayList<Ball> ballList;
float gravity;

void setup() {
    size(500, 500);

    ballList = new ArrayList<Ball>();

    gravity = 0.1;
}

void draw() {
    background(255);

    for (Ball b : ballList) {
        b.render();
        b.dy += gravity;
    }
}

void mouseClicked() {
    ballList.add(new Ball());
}

21.8. Accessing Individual Elements of an ArrayList

Sometimes you do want to access individual elements in an array, rather than serially accessing all of them. For example, suppose we have this ArrayList:

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

names.add("James");
names.add("Lars");
names.add("Dave");
names.add("Ron");

Recall that each object in an ArrayList has a position in the list, also known as its index. The first element is at index 0 (NOT 1!), the second element is at index 1, and so on.

We’ve already seen how to use get to access an element: names.get(1) gives back the String “Lars”.

Another useful function for ArrayLists is the set(index, object) which allows us to place object at a particular index:

names.set(3, "Cliff"); // change "Ron" to "Cliff"
names.set(2, "Kirk"); // change "Dave" to "Kirk"
names.set(3, "Jason"); // change "Cliff" to "Jason" :-(
names.set(3, "Rob"); // change "Jason" to "Rob"

This results in the final list being:

position String
0 “James”
1 “Lars”
2 “Kirk”
3 “Rob”

Warning

The set function will only accept index values that are less than the index of the last element in the array. For example, in the ArrayList from the example above, calling names.set(4, "Oren") results in error. Processing will complain that we are out of bounds. To add an element to an ArrayList you must use the add function.

Setting and getting can be mixed in ways that can be tricky to understand if you are not careful. For example, here’s a useful fragment of code that shows how to swap the objects at locations 0 and 1 of an ArrayList:

String temp = names.get(0); // record element 0
names.set(0, names.get(1));
names.set(1, temp);

Finally, functions like set and get are common enough in programming that they are often referred to as setters and getters.

21.9. Using ArrayLists with Primitive Types

Due a quirk of Java, an ArrayList can’t be used to hold the basic primitive types like int, float, or char. For example, both of these lines cause compiler error:

ArrayList<int> ages; // error: can’t make an ArrayList of ints

ArrayList<float> temps; // error: can’t make an ArrayList of floats

The problem is that int and float are not object types like String or Ball, and an ArrayList can only contain object types.

Fortunately, there is a relatively simple work-around. For ints we can write Integer instead of int when declaring an ArrayList:

ArrayList<Integer> ages;
ages = new ArrayList<Integer>();

ages.add(18);
ages.add(19);
int twenty = 20;
ages.add(twenty);

for (int n : ages) {
    println(n);
}

Similarly, we use Float instead of float when we want to work with ArrayLists.

What’s going on here is that Integer is a wrapper class for int. An Integer object essentially just contains a regular int, which allows us to place integers into an ArrayList. You can imagine yourself defining an Integer using the class definition:

class Integer {
    int value;

    // a constructor
    // some getters and setters, etc.
}

Similar reasoning applies to the Float object.

Historically, basic types like int and float were not implemented as objects for performance concerns.

21.10. Summary: Using ArrayLists

In general, to use an ArrayList, you need to do the following:

  1. Declare the ArrayList variable, e.g.:

    ARrayList<String> names; // initially null
    

    To create an array list of integers, you must use ArrayList<Integer> and not ArrayList<int>. Similarly, ArrayList<Float> must be used instead of ArrayList<float>.

  2. Create the ArrayList object, e.g.:

    names = new ArrayList<String>();  // initially empty
    

    Notice that we use the same type, <String> in both the declaration of the object and the initialization of the object.

  3. Use add to append objects to the end of the ArrayList:

    names.add("Pinky");
    names.add("Brain");
    
  4. Use a for-each loop to run a block of code on each element of an ArrayList, e.g.:

    for (String name : names) {
        println(n);
    }
    

    Notice that the type of the variable name is String, which is the same type of object as those that the ArrayList names contains.

    For-each loops are simple and useful, but not very flexible. For instance, if you only want to process some of the elements in an ArrayList, the for-each loop can’t help you: it always loops through all the elements. This is why Processing has other kinds of loops. We’ll learn about while-loops a little later.

Note

Processing also has arrays, which is another way to holds collections of values. Arrays are a lower-level feature as compared to ArrayLists, and so sometimes are a little more efficient. However, the main difficulty with arrays is that they are fixed in size. To use them, you must know in advance the number of items they will contain ahead of time.

In contrast, you can add as many items as you need to an ArrayList, at any time you need to add them. An ArrayList automatically expands its size as needed

Note

Technically, the angle-brackets at the end of an ArrayList declaration are optional. That is, you can write code like this for example:

ArrayList ballList;  // bad: no object type given!

ballList = new ArrayList();

And processing won’t stop you! The problem with this is that the ArrayList does not know anything about the type of objects that ce be store in it. You can add any type of objects to such an ArrayList. For example:

ArrayList ballList; // bad: no object type given!

ballList = new ArrayList();

ballList.add(new Ball()); ballList.add(“This is a String not a Ball”);

Now ballList contains a Ball object and a String object, and if we want to avoid errors, we now how have to be very careful when accessing the elements of ballList. For example the String object will have a length() function that the Ball object does not, and if we forget that the first element in ballList was a Ball, we might try to acess this length() function, and our program will come to grinding stop.

So, in this course, we will never use ArrayList without the angle-brackets declaring what type of object it can contain.

21.11. Questions

  1. What happens if you run this code in Processing?

    ArrayList<String> names;
    names = new ArrayList<BouncingBall>();
    
  2. What does this code print?

    ArrayList<String> names;
    for(String s : names) {
       println(names);
    }
    
  3. Suppose names is an ArrayList<String> object. Write a fragment of code that prints (using println) the length of each string in it.

  4. Suppose you have a class called Hopper for animating hopping frogs. To create a single frog you can do this:

    Hopper frog = new Hopper();
    

    Hopper objects also have a render() function for drawing, and an update function for updating their state.

    Answer each of the following questions:

    1. Define a new variable called pond that can refer to an ArrayList of Hopper objects. Just create the variable, not the ArrayList.
    2. Using the pond variable from the previous question, create a new ArrayList that can contain just Hopper objects. It should be initially empty.
    3. Write code to add three new Hopper objects to pond.
    4. Write a for-each loop that renders and updates all the Hopper objects in pond.
  5. Suppose nums is an ArrayList<Float> containing some numbers. Write a fragment of code that returns the sum of all the numbers in nums.

  6. Suppose nums is an ArrayList<Float> containing some numbers. Write a fragment of code that prints (using println) the number of values in nums that are greater than 0.

    For example, if nums contained the values {6.4, -14. 0, 8, -2}, then your could should print 2.

  7. Suppose names in ArrayList<String> with at least 100 elements in it. Write a fragment of code that:

    1. Swaps the element at position 4 with the element at position 5.
    2. Swaps the element at position 10 with the element at position 2.
  8. Modify the program in the notes that adds balls when the mouse is clicked so that the added ball is initially positioned at the mouse pointer.

21.12. The Complete Sample Program

This program creates a ball randomly whenever the user clicks the mouse button.

class Ball {

    // a ball has an (x, y) position
    float x;
    float y;

    // and a diameter
    float diam;

    // and a color
    color myColor;

    // and speed variables
    float dx, dy;

    Ball(float initX, float initY, float initDiam, color initColor) {
        x = initX;
        y = initY;
        diam = initDiam;
        myColor = initColor;
    }

    // Constructor to build a random ball
    Ball() {

        // We'll insist that the ball starts close to the center of the
        // screen.
        x = random(200, 300);
        y = random(200, 300);

        diam = random(25, 75);

        dx = random(-2.0, 2.0);
        dy = random(-2.0, 2.0);

        // random color
        float rred = random(0, 256);
        float rgreen = random(0, 256);
        float rblue = random(0, 256);

        ballColor = color(rred, rblue, rgreen);
    }

    void render() {
        fill(myColor);
        noStroke();
        ellipse(x, y, diam, diam);

        x += dx;
        y += dy;

        // hit top?
        if (y - diam / 2 <= 0) {
            y = diam / 2;
            dy = -dy;
        }

        // hit bottom?
        if (y + diam / 2 >= height - 1) {
            y = height - 1 - diam / 2;
            dy = -dy;
        }

        // hit left?
        if (x - diam / 2 <= 0) {
            x = diam / 2;
            dx = -dx;
        }

        // hit right?
        if (x + diam / 2 >= width - 1) {
            x = width - 1 - diam / 2;
            dx = -dx;
        }

    }
}

ArrayList<Ball> ballList;
float gravity;

void setup() {
    size(500, 500);

    ballList = new ArrayList<Ball>();

    gravity = 0.1;
}

void draw() {
    background(255);

    for (Ball b : ballList) {
        b.render();
        b.dy += gravity;
    }
}

void mouseClicked() {
    ballList.add(new Ball());
}