An array is a list of variables that share a common name. Arrays are useful because they make it possible to work with more variables without creating a new name for each. This makes the code shorter, easier to read, and more convenient to update.
When a program needs to keep track of one or two things, it’s not necessary to use an array. In fact, adding an array might make the program more complicated than necessary. However, when a program has many elements (for example, a field of stars in a space game or multiple data points in a visualization), arrays make the code easier to write.
To see what we mean, refer to Example 8-3. This code works fine if we’re moving around only one shape, but what if we want to have two? We need to make a new x
variable and update it within draw()
:
float
x1
=
-
20
;
float
x2
=
20
;
void
setup
()
{
size
(
240
,
120
);
noStroke
();
}
void
draw
()
{
background
(
0
);
x1
+=
0.5
;
x2
+=
0.5
;
arc
(
x1
,
30
,
40
,
40
,
0.52
,
5.76
);
arc
(
x2
,
90
,
40
,
40
,
0.52
,
5.76
);
}
The code for the previous example is still manageable, but what if we want to have five circles? We need to add three more variables to the two we already have:
float
x1
=
-
10
;
float
x2
=
10
;
float
x3
=
35
;
float
x4
=
18
;
float
x5
=
30
;
void
setup
()
{
size
(
240
,
120
);
noStroke
();
}
void
draw
()
{
background
(
0
);
x1
+=
0.5
;
x2
+=
0.5
;
x3
+=
0.5
;
x4
+=
0.5
;
x5
+=
0.5
;
arc
(
x1
,
20
,
20
,
20
,
0.52
,
5.76
);
arc
(
x2
,
40
,
20
,
20
,
0.52
,
5.76
);
arc
(
x3
,
60
,
20
,
20
,
0.52
,
5.76
);
arc
(
x4
,
80
,
20
,
20
,
0.52
,
5.76
);
arc
(
x5
,
100
,
20
,
20
,
0.52
,
5.76
);
}
This code is starting to get out of control.
Imagine what would happen if you wanted to have 3,000 circles. This would mean creating 3,000 individual variables, then updating each one separately. Could you keep track of that many variables? Would you want to? Instead, we use an array:
float
[]
x
=
new
float
[
3000
];
void
setup
()
{
size
(
240
,
120
);
noStroke
();
fill
(
255
,
200
);
for
(
int
i
=
0
;
i
<
x
.
length
;
i
++)
{
x
[
i
]
=
random
(-
1000
,
200
);
}
}
void
draw
()
{
background
(
0
);
for
(
int
i
=
0
;
i
<
x
.
length
;
i
++)
{
x
[
i
]
+=
0.5
;
float
y
=
i
*
0.4
;
arc
(
x
[
i
],
y
,
12
,
12
,
0.52
,
5.76
);
}
}
We’ll spend the rest of this chapter talking about the details that make this example possible.
Each item in an array is called an element, and each has an index value to mark its position within the array. Just like coordinates on the screen, index values for an array start counting from 0. For instance, the first element in the array has the index value 0, the second element in the array has the index value 1, and so on. If there are 20 values in the array, the index value of the last element is 19. Figure 11-1 shows the conceptual structure of an array.
Using arrays is similar to working with single variables; it follows the same patterns. As you know, you can make a single integer variable called x
with this code:
int
x
;
To make an array, just place brackets after the data type:
int
[]
x
;
The beauty of creating an array is the ability to make 2, 10, or 100,000 variable values with only one line of code. For instance, the following line creates an array of 2,000 integer variables:
int
[]
x
=
new
int
[
2000
];
You can make arrays from all Processing data types: boolean
, float, String
, PShape
, and so on, as well as any user-defined class
. For example, the following code creates an array of 32 PImage
variables:
PImage
[]
images
=
new
PImage
[
32
];
To make an array, start with the name of the data type, followed by the brackets. The name you select for the array is next, followed by the assignment operator (the equal symbol), followed by the new
keyword, followed by the name of the data type again, with the number of elements to create within the brackets. This pattern works for arrays of all data types.
Each array can store only one type of data (boolean
, int
, float
, PImage
, etc.). You can’t mix and match different types of data within a single array. If you need to do this, work with objects instead.
Before we get ahead of ourselves, let’s slow down and talk about working with arrays in more detail. Like making an object, there are three steps to working with an array:
Declare the array and define the data type.
Create the array with the keyword new
and define the length.
Assign values to each element.
Each step can happen on its own line, or all the steps can be compressed together. Each of the three following examples shows a different technique to create an array called x
that stores two integers, 12 and 2. Pay close attention to what happens before setup()
and what happens within setup()
.
First, we’ll declare the array outside of setup()
and then create and assign the values within. The syntax x[0]
refers to the first element in the array and x[1]
is the second:
int
[]
x
;
// Declare the array
void
setup
()
{
size
(
200
,
200
);
x
=
new
int
[
2
];
// Create the array
x
[
0
]
=
12
;
// Assign the first value
x
[
1
]
=
2
;
// Assign the second value
}
Here’s a slightly more compact example, in which the array is both declared and created on the same line, then the values are assigned within setup()
:
int
[]
x
=
new
int
[
2
];
// Declare and create the array
void
setup
()
{
size
(
200
,
200
);
x
[
0
]
=
12
;
// Assign the first value
x
[
1
]
=
2
;
// Assign the second value
}
You can also assign values to the array when it’s created, if it’s all part of a single statement:
int
[]
x
=
{
12
,
2
};
// Declare, create, and assign
void
setup
()
{
size
(
200
,
200
);
}
As a complete example of how to use arrays, we’ve recoded Example 11-1 here. Although we don’t yet see the full benefits revealed in Example 11-3, we do see some important details of how arrays work:
float
[]
x
=
{-
20
,
20
};
void
setup
()
{
size
(
240
,
120
);
noStroke
();
}
void
draw
()
{
background
(
0
);
x
[
0
]
+=
0.5
;
// Increase the first element
x
[
1
]
+=
0.5
;
// Increase the second element
arc
(
x
[
0
],
30
,
40
,
40
,
0.52
,
5.76
);
arc
(
x
[
1
],
90
,
40
,
40
,
0.52
,
5.76
);
}
The for
loop, introduced in “Repetition”, makes it easier to work with large arrays while keeping the code concise. The idea is to write a loop to move through each element of the array one by one. To do this, you need to know the length of the array. The length
field associated with each array stores the number of elements. We use the name of the array with the dot operator (a period) to access this value. For instance:
int
[]
x
=
new
int
[
2
];
// Declare and create the array
println
(
x
.
length
);
// Prints 2 to the Console
int
[]
y
=
new
int
[
1972
];
// Declare and create the array
println
(
y
.
length
);
// Prints 1972 to the Console
A for
loop can be used to fill an array with values, or to read the values back out. In this example, the array is first filled with random numbers inside setup()
, and then these numbers are used to set the stroke value inside draw()
. Each time the program is run, a new set of random numbers is put into the array:
float
[]
gray
;
void
setup
()
{
size
(
240
,
120
);
gray
=
new
float
[
width
];
for
(
int
i
=
0
;
i
<
gray
.
length
;
i
++)
{
gray
[
i
]
=
random
(
0
,
255
);
}
}
void
draw
()
{
for
(
int
i
=
0
;
i
<
gray
.
length
;
i
++)
{
stroke
(
gray
[
i
]);
line
(
i
,
0
,
i
,
height
);
}
}
In this example, there are two arrays to store the position of the mouse—one for the x coordinate and one for the y coordinate. These arrays store the location of the mouse for the previous 60 frames. With each new frame, the oldest x and y coordinate values are removed and replaced with the current mouseX
and mouseY
values. The new values are added to the first position of the array, but before this happens, each value in the array is moved one position to the right (from back to front) to make room for the new numbers. This example visualizes this action. Also, at each frame, all 60 coordinates are used to draw a series of ellipses to the screen:
int
num
=
60
;
int
[]
x
=
new
int
[
num
];
int
[]
y
=
new
int
[
num
];
void
setup
()
{
size
(
240
,
120
);
noStroke
();
}
void
draw
()
{
background
(
0
);
// Copy array values from back to front
for
(
int
i
=
x
.
length
-
1
;
i
>
0
;
i
--)
{
x
[
i
]
=
x
[
i
-
1
];
y
[
i
]
=
y
[
i
-
1
];
}
x
[
0
]
=
mouseX
;
// Set the first element
y
[
0
]
=
mouseY
;
// Set the first element
for
(
int
i
=
0
;
i
<
x
.
length
;
i
++)
{
fill
(
i
*
4
);
ellipse
(
x
[
i
],
y
[
i
],
40
,
40
);
}
}
The technique for storing a shifting buffer of numbers in an array shown in this example and Figure 11-2 is less efficient than an alternative technique that uses the % (modulo) operator. This is explained in the Examples → Basics → Input → StoringInput example included with Processing.
The two short examples in this section bring together every major programming concept in this book: variables, iteration, conditionals, functions, objects, and arrays. Making an array of objects is nearly the same as making the arrays we introduced on the previous pages, but there’s one additional consideration: because each array element is an object, it must first be created with the keyword new
(like any other object) before it is assigned to the array. With a custom-defined class
such as JitterBug
(see Chapter 10), this means using new
to set up each element before it’s assigned to the array. Or, for a built-in Processing class
such as PImage
, it means using the loadImage()
function to create the object before it’s assigned.
This example creates an array of 33 JitterBug
objects and then updates and displays each one inside draw()
. For this example to work, you need to add the JitterBug
class
to the code:
JitterBug
[]
bugs
=
new
JitterBug
[
33
];
void
setup
()
{
size
(
240
,
120
);
for
(
int
i
=
0
;
i
<
bugs
.
length
;
i
++)
{
float
x
=
random
(
width
);
float
y
=
random
(
height
);
int
r
=
i
+
2
;
bugs
[
i
]
=
new
JitterBug
(
x
,
y
,
r
);
}
}
void
draw
()
{
for
(
int
i
=
0
;
i
<
bugs
.
length
;
i
++)
{
bugs
[
i
].
move
();
bugs
[
i
].
display
();
}
}
// Insert JitterBug class from Example 10-1
When working with arrays of objects, there’s a different kind of loop to use called an “enhanced” for
loop. Instead of creating a new counter variable, such as the i
variable in Example 11-10, it’s possible to iterate over the elements of an array or list directly. In the following example, each object in the bugs
array of JitterBug
objects is assigned to b
in order to run the move()
and display()
methods for all objects in the array.
The enhanced for
loop is often tidier than looping with a number, although in this example, we didn’t use it inside setup()
because i
was needed in two places inside the loop, demonstrating how sometimes it’s helpful to have the number around:
JitterBug
[]
bugs
=
new
JitterBug
[
33
];
void
setup
()
{
size
(
240
,
120
);
for
(
int
i
=
0
;
i
<
bugs
.
length
;
i
++)
{
float
x
=
random
(
width
);
float
y
=
random
(
height
);
int
r
=
i
+
2
;
bugs
[
i
]
=
new
JitterBug
(
x
,
y
,
r
);
}
}
void
draw
()
{
for
(
JitterBug
b
:
bugs
)
{
b
.
move
();
b
.
display
();
}
}
// Insert JitterBug class from Example 10-1
The final array example loads a sequence of images and stores each as an element within an array of PImage
objects.
To run this example, get the images from the media.zip file as described in Chapter 7. The images are named sequentially (frame-0000.png, frame-0001.png, and so forth), which makes it possible to create the name of each file within a for
loop, as seen in the eighth line of the program:
int
numFrames
=
12
;
// The number of frames
PImage
[]
images
=
new
PImage
[
numFrames
];
// Make the array
int
currentFrame
=
0
;
void
setup
()
{
size
(
240
,
120
);
for
(
int
i
=
0
;
i
<
images
.
length
;
i
++)
{
String
imageName
=
"frame-"
+
nf
(
i
,
4
)
+
".png"
;
images
[
i
]
=
loadImage
(
imageName
);
// Load each image
}
frameRate
(
24
);
}
void
draw
()
{
image
(
images
[
currentFrame
],
0
,
0
);
currentFrame
++;
// Next frame
if
(
currentFrame
>=
images
.
length
)
{
currentFrame
=
0
;
// Return to first frame
}
}
The nf()
function formats numbers so that nf(1, 4)
returns the string “0001” and nf(11, 4)
returns “0011”. These values are concatenated with the beginning of the filename (frame-) and the end (.png) to create the complete filename as a String
variable. The files are loaded into the array on the following line. The images are displayed to the screen one at a time in draw()
. When the last image in the array is displayed, the program returns to the beginning of the array and shows the images again in sequence.
Arrays make it easier for a program to work with many elements. In this example, an array of Robot
objects is declared at the top. The array is then allocated inside setup()
, and each Robot
object is created inside the for
loop. In draw()
, another for
loop is used to update and display each element of the bots
array.
The for
loop and an array make a powerful combination. Notice the subtle differences between the code for this example and Robot 8 (see “Robot 8: Objects”) in contrast to the extreme changes in the visual result. Once an array is created and a for
loop is put in place, it’s as easy to work with 3 elements as it is 3,000.
The decision to load the SVG file within setup()
rather than in the Robot
class
is the major change from Robot 8. This choice was made so the file is loaded only once, rather than as many times as there are elements in the array (in this case, 20 times). This change makes the code start faster because loading a file takes time, and it uses less memory because the file is stored once. Each element of the bot
array references the same file:
Robot
[]
bots
;
// Declare array of Robot objects
void
setup
()
{
size
(
720
,
480
);
PShape
robotShape
=
loadShape
(
"robot2.svg"
);
// Create the array of Robot objects
bots
=
new
Robot
[
20
];
// Create each object
for
(
int
i
=
0
;
i
<
bots
.
length
;
i
++)
{
// Create a random x coordinate
float
x
=
random
(-
40
,
width
-
40
);
// Assign the y coordinate based on the order
float
y
=
map
(
i
,
0
,
bots
.
length
,
-
100
,
height
-
200
);
bots
[
i
]
=
new
Robot
(
robotShape
,
x
,
y
);
}
}
void
draw
()
{
background
(
0
,
153
,
204
);
// Update and display each bot in the array
for
(
int
i
=
0
;
i
<
bots
.
length
;
i
++)
{
bots
[
i
].
update
();
bots
[
i
].
display
();
}
}
class
Robot
{
float
xpos
;
float
ypos
;
float
angle
;
PShape
botShape
;
float
yoffset
=
0.0
;
// Set initial values in constructor
Robot
(
PShape
shape
,
float
tempX
,
float
tempY
)
{
botShape
=
shape
;
xpos
=
tempX
;
ypos
=
tempY
;
angle
=
random
(
0
,
TWO_PI
);
}
// Update the fields
void
update
()
{
angle
+=
0.05
;
yoffset
=
sin
(
angle
)
*
20
;
}
// Draw the robot to the screen
void
display
()
{
shape
(
botShape
,
xpos
,
ypos
+
yoffset
);
}
}