Bouncing Balls, Lists, and Loops¶
In these notes you will learn:
- How to define
ArrayList
s. - 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
.
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. Creating and using 100
variables is extremely tedious, and so in these notes we will see how to store
multiple objects in an ArrayList
s and then process them using a loop.
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.
By default, ballList
is 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
ArrayList
s, 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.
Creating ArrayList
Objects¶
To actually create an ArrayList
object, we use new
:
ArrayList<BouncingBall> ballList; // ballList variable is null initially
void setup() {
// ...
ballList = new ArrayList<BouncingBall>();
}
Here’s how you could initialize an ArrayList
of String
s:
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.
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");
ArrayList
s 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.
Processing an ArrayList
with a For-each Loop¶
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 :
is read “in”, and so the statement
for(BouncingBall b : ballList)
is 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
automatically 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
per line.
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() {
BouncingBall b = randomBouncingBall();
b.x = mouseX;
b.y = mouseY;
ballList.add(b);
}
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 tricky ways. 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 object-
oriented programming that they are referred to as setters and getters.
Using ArrayList
s 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 ArrayList
s.
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.
Summary: Using ArrayList
s¶
In general, to use an ArrayList
, you need to do the following:
Declare the
ArrayList
variable, e.g.:ArrayList<String> names; // initially null
To create an
ArrayList
of integers, you must useArrayList<Integer>
and notArrayList<int>
. Similarly,ArrayList<Float>
must be used instead ofArrayList<float>
.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 theArrayList
object.Use
add
to append objects to the end of theArrayList
, e.g.:names.add("Sammy"); names.add("Andie"); names.add("Nelly");
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
isString
, which is the same as the type of object of that theArrayList
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.
Questions¶
What happens if you run this code in Processing?
ArrayList<String> names; names = new ArrayList<BouncingBall>();
What does this code print?
ArrayList<String> names; for(String s : names) { println(names); }
Suppose
names
is anArrayList<String>
object. Write a fragment of code that prints (usingprintln
) the length of each string in it.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 arender()
function for drawing, and anupdate
function for updating their state.Answer each of the following questions:
- Define a new variable called
pond
that can refer to anArrayList
ofHopper
objects. Just create the variable, not theArrayList
. - Using the
pond
variable from the previous question, create a newArrayList
that can contain justHopper
objects. It should be initially empty. - Write code to add three new
Hopper
objects topond
. - Write a for-each loop that renders and updates all the
Hopper
objects inpond
.
- Define a new variable called
Suppose
nums
is anArrayList<Float>
containing some numbers. Write a fragment of code that returns the sum of all the numbers innums
.Suppose
nums
is anArrayList<Float>
containing some numbers. Write a fragment of code that prints (usingprintln
) the number of values innums
that are greater than 0.For example, if
nums
contained the values{6.4, -14. 0, 8, -2}
, then your could should print 2.Suppose
names
inArrayList<String>
with at least 100 elements in it. Write a fragment of code that:- Swaps the element at position 4 with the element at position 5.
- Swaps the element at position 10 with the element at position 2.
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.
The Complete Sample Program¶
class Sprite {
float x;
float y;
float dx;
float dy;
void update() {
x += dx;
y += dy;
}
}
class BouncingBall extends Sprite {
float diam;
color fillColor;
void render() {
pushMatrix();
noStroke();
fill(fillColor);
ellipse(x, y, diam, diam);
popMatrix();
}
void update() {
x += dx;
y += dy;
// hit top?
if (y - diam / 2 < 0) {
y = diam / 2;
dy = -dy;
}
// hit bottom?
if (y + diam / 2 > height) {
y = height - diam / 2;
dy = -dy;
}
// hit left?
if (x - diam / 2 < 0) {
x = diam / 2;
dx = -dx;
}
// hit right?
if (x + diam / 2 > width) {
x = width - diam / 2;
dx = -dx;
}
}
}
BouncingBall randomBouncingBall() {
BouncingBall ball = new BouncingBall();
ball.x = random(100, 200);
ball.y = random(100, 200);
ball.dx = random(-3, 3);
ball.dy = random(-3, 3);
ball.diam = random(50, 150);
ball.fillColor = color(random(255),
random(255),
random(255));
return ball;
}
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() {
BouncingBall b = randomBouncingBall();
b.x = mouseX;
b.y = mouseY;
ballList.add(b);
}