24. Bouncing Balls, Lists, and Loops

In these notes you will learn:

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

24.1. Introduction

We saw in the previous notes how to use objects to make one ball bounce around the screen. As long as you only want a few balls on the screen, it worked great. But if you want lots of balls at the same time, say 100, then using what we know about programming so far we’d have to declare 100 different named variables in it. Such programs are far too long and tedious for people to write, and so in these notes we will see how to store multiple objects in an ArrayLists and then process them using a loop.

24.2. Defining ArrayList Variables

To handle multiple BouncingBall objects at the same time, we’ll store them in a container object know as an ArrayList:

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

This statement creates a variable called ballList that can be used to refer to an ArrayList of BouncingBall objects.

We have not created an ArrayList object — just the variable that can refer to it.

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

You can create an ArrayList variable to hold any type of object. For example:

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

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

Note

Processing also has arrays, which is another way to hold collections of values. Arrays are a lower-level feature as compared to ArrayLists, and so can sometimes be a little more efficient. But the main difficulty with arrays is that they are fixed in size, and so require that you know the maximum 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 it size as needed.

24.3. Creating ArrayList Objects

To actually create an ArrayList object, we use new:

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

void setup() {
   // ...

   ballList = new ArrayList<BouncingBall>();
}

Note that after ArrayList we write BouncingBall in angle-brackets < >. This means that the ArrayList can only contain objects of type BouncingBall; you’ll get an error if you try to add an object not of type BouncingBall to ballList.

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

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

void setup() {
   // ...

   names = new ArrayList<String>();
}

Note

Technically, the angle-brackets at the end of an ArrayList declaration are optional, e.g. you can write code like this:

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

ballList = new ArrayList();

The problem with this is that the ArrayList does not know anything about the type of objects that can be stored in it. You can add any type of object to such an ArrayList, e.g.:

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

ballList = new ArrayList();

ballList.add(randomBouncingBall());
ballList.add("this is not a ball");

Now ballList contains a BouncingBall ball object and a String object. While this is occasionally useful, in practice mixing different types of objects in the same ArrayList tends to cause subtle bugs.

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

It is unfortunate that Processing (and Java) allow both forms. Processing even recommends this second form in some of its documentation. However, the version without the angle-brackets is a legacy feature that is rarely useful.

24.4. Adding Elements to an ArrayList

The add function is used to append new elements to the end of an ArrayList:

ArrayList<BouncingBall> ballList;  // create an initially null variable

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

ballList.add(randomBouncingBall());        // add some BouncingBall
ballList.add(randomBouncingBall());        // objects
ballList.add(randomBouncingBall());

Here’s how we could use names to store names as strings:

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

names = new ArrayList<String>();  // create an initially empty ArrayList
                                  // of strings and assign it to names

names.add("Julius Caesar");
names.add("William Shakespeare");
names.add("Tony Clifton");

ArrayLists can contain as many objects as you add to them (within the memory limits of your computer, of course). They start out empty and then 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 just add each name as you get it.

24.5. Processing an ArrayList with a For-each Loop

Now that we know how to create ArrayLists and add objects to them, lets 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(BouncingBall b : ballList) {  // for-each loop
   b.render();
   b.update();
}

Inside a loop like this, the : can be read as the word “in”, and so the statement for(BouncingBall b : ballList) can be read as “for each bouncing 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) {  // for-each loop
   println(s);
}

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

24.6. Multiple Bouncing Balls

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

ArrayList<BouncingBall> ballList;  // initially null

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

  // create the initially empty ArrayList of BouncingBall objects
  ballList = new ArrayList<BouncingBall>();
}

void draw() {
  background(255);

  // render and update all the balls
  for(BouncingBall b : ballList) {
    b.render();
    b.update();
  }
}


void mousePressed() {
  ballList.add(randomBouncingBall());
}

24.7. Accessing Individual Elements of an ArrayList

You access individual elements of an ArrayList use set and get. For example, suppose we have this ArrayList:

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

names.add("Julius");    // location 0
names.add("William");   // location 1
names.add("Tony");      // location 2

Each object in an ArrayList has a location, also know as an index location (or index position). The first element in an ArrayList is always 0 — not 1! The second element is at location 1 (not 2), the third element is at location 2, and so on.

Using the ArrayList get and set functions we can efficiently access elements by specifying their location.

For example, we can use get to access any individual element of names, e.g.:

println(names.get(0));   // print 1st element: "Julius"
println(names.get(1));   // print 2nd element: "William"
println(names.get(2));   // print 3rd element: "Tony"

It’s essential to remember that the first element of an ArrayList is always at location 0, and so names.get(0)

If we want to put a new object into a particular ArrayList location, we use set, e.g.:

names.set(0, "Augustus");
names.set(1, "Bill");
names.set(2, "Andy");

println(names.get(0));   // print 1st element: "Augustus"
println(names.get(1));   // print 2nd element: "Bill"
println(names.get(2));   // print 3rd element: "Andy"

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);  // remember 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 in general as setters and getters.

24.8. Using ArrayLists with Primitive Types

Due to a quirk of Java, an ArrayList can’t contain basic built-in types like int, float, or char. For example, both of these lines cause compiler errors:

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 and BouncingBall are, and an ArrayList can only contain object types.

Fortunately, there is a relatively simple work-around. Use Integer instead of int when declaring an ArrayList. e.g.:

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

ages.add(18);
ages.add(19);
ages.add(11);

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

Similarly, use Float instead of float when working with ArrayLists.

What’s going on here is that Integer is a wrapper class for int. An Integer object essentially contains a regular int, but because it is in an object it can be used in an ArrayList and similar containers.

Historically, basic types like int and float were not implemented as objects over performance concerns, i.e. when Java was originally implemented the designers felt it would be too slow to make int and float full- blown objects.

24.9. 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 ArrayList 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 used the same type, ArrayList<String>, in both the declaration of the variable and the create of the ArrayList object.

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

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

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

    Notice that the type of the variable n is String, which is the same as the type of object of 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 element. Thus, Processing has other kinds of loops. We’ll learn about while-loops a little later in the course.

24.10. 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.

24.11. The Complete Sample Program

class BouncingBall {
  float x;   // (x, y) is the center of the ball
  float y;
  float dx;  // (dx, dy) is the velocity of the ball
  float dy;
  float diameter;

  color c;

  BouncingBall(float init_x, float init_y,
               float init_dx, float init_dy,
               float init_diameter,
               color init_c)
  {
    x = init_x;
    y = init_y;
    dx = init_dx;
    dy = init_dy;
    diameter = init_diameter;
    c = init_c;
  }

  void render() {
    noStroke();
    fill(c);
    ellipse(x, y, diameter, diameter);
  }

  void update() {
    // move the ball to its next position
    x += dx;
    y += dy;

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

    // hit the bottom edge?
    if (y > 499) {
      y = 499;
      dy = -dy;
    }

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

    // hit the right edge?
    if (x > 499) {
      x = 499;
      dx = -dx;
    }
  }
} // class BouncingBall


BouncingBall randomBouncingBall() {
  return new BouncingBall(random(200, 300), random(200, 300),
                          random(-2.0, 2.0), random(-2.0, 2.0),
                          random(25, 76),
                          color(random(0, 256),
                                random(0, 256),
                                random(0, 256))
                         );
}


ArrayList<BouncingBall> ballList;  // initially null

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

  // create the initially empty ArrayList of BouncingBall objects
  ballList = new ArrayList<BouncingBall>();
}

void draw() {
  background(255);

  // render and update all the balls
  for(BouncingBall b : ballList) {
    b.render();
    b.update();
  }
}


void mousePressed() {
  ballList.add(randomBouncingBall());
}