In this chapter:
– Declaring an array
– Initializing an array
– Array operations – using a for loop with an array
– Arrays of objects
Let’s take a moment to revisit the car example from the previous chapter on object-oriented programming. You may remember I spent a great deal of effort on developing a program that contained multiple instances of a class, that is, two objects.
Car myCar1;
Car myCar2;
This was indeed an exciting moment in the development of your life as a computer programmer. It’s likely, however, that you’re contemplating a somewhat obvious question. How could you take this further and write a program with 100 Car objects? With some clever copying and pasting, you might write a program with the following beginning:
Car myCar1
Car myCar2
Car myCar3
Car myCar4
Car myCar5
Car myCar6
Car myCar7
Car myCar8
Car myCar9
Car myCar10
Car myCar11
Car myCar12
Car myCar13
Car myCar14
Car myCar15
Car myCar16
Car myCar17
Car myCar18
Car myCar19
Car myCar21
Car myCar22
Car myCar23
Car myCar24
Car myCar25
Car myCar26
Car myCar27
Car myCar28
Car myCar29
Car myCar30
Car myCar31
Car myCar32
Car myCar33
Car myCar34
Car myCar35
Car myCar36
Car myCar37
Car myCar38
Car myCar39
Car myCar40
Car myCar41
Car myCar42
Car myCar43
Car myCar44
Car myCar45
Car myCar46
Car myCar47
Car myCar48
Car myCar49
Car myCar50
Car myCar51
Car myCar52
Car myCar53
Car myCar54
Car myCar55
Car myCar56
Car myCar57
Car myCar58
Car myCar59
Car myCar60
Car myCar61
Car myCar62
Car myCar63
Car myCar64
Car myCar65
Car myCar66
Car myCar67
Car myCar69
Car myCar70
Car myCar71
Car myCar72
Car myCar73
Car myCar74
Car myCar75
Car myCar76
Car myCar77
Car myCar78
Car myCar79
Car myCar80
Car myCar81
Car myCar82
Car myCar83
Car myCar84
Car myCar85
Car myCar86
Car myCar87
Car myCar88
Car myCar89
Car myCar90
Car myCar91
Car myCar92
Car myCar93
Car myCar94
Car myCar95
Car myCar96
Car myCar97
Car myCar98
Car myCar99
Car myCar100
If you really want to give yourself a headache, try completing the rest of the program modeled after the above start. It will not be a pleasant endeavor. I am certainly not about to leave you any workbook space in this book to practice.
An array will allow you to take these 100 lines of code and put them into one line. Instead of having 100 variables, an array is one thing that contains a list of variables.
Any time a program requires multiple instances of similar data, it might be time to use an array. For example, an array can be used to store the scores of four players in a game, a selection of 10 colors in a design program, or a list of fish objects in an aquarium simulation.
From Chapter 4, you may recall that a variable is a named pointer to a location in memory where data is stored. In other words, variables allow programs to keep track of information over a period of time. An array is exactly the same, only instead of pointing to one singular piece of information, an array points to multiple pieces. See Figure 9-1.
You can think of an array as a list of variables. A list, it should be noted, is useful for two important reasons. Number one, the list keeps track of the elements in the list themselves. Number two, the list keeps track of the order of those elements (which element is the first in the list, the second, the third, etc.). This is a crucial point since in many programs, the order of information is just as important as the information itself.
In an array, each element of the list has a unique index, an integer value that designates its position in the list (element #1, element #2, etc.). In all cases, the name of the array refers to the list as a whole, while each element is accessed via its position.
Notice how in Figure 9-2, the indices range from 0 to 9. The array has a total of 10 elements, but the first element number is 0 and the last element is 9. You might be tempted to stomp your feet and complain: “Hey, why aren’t the elements numbered from 1 to 10? Wouldn’t that be easier?”
While at first, it might intuitively seem like I should start counting at 1 (and some programming languages do), I start at 0 because technically the first element of the array is located at the start of the array, a distance of zero from the beginning. Numbering the elements starting at 0 also makes many array operations (the process of executing a line of code for every element of the list) a great deal more convenient. As I continue through several examples, you will begin to believe in the power of counting from zero.
In Chapter 4, you learned that all variables must have a name and a data type. Arrays are no different. The declaration statement, however, does look different. You denote the use of an array by placing empty square brackets ([]) after the type declaration. Let’s start with an array of primitive values, for example, integers. (You can have arrays of any data type, and I will soon show how you can make an array of objects.) See Figure 9-3.
The declaration in Figure 9-3 indicates that arrayOfInts will store a list of integers. The array name arrayOfInts can be absolutely anything you want it to be (I only include the word “array” here to illustrate what you are learning).
One fundamental property of arrays, however, is that they are of fixed size. Once I define the size for an array, it can never change. A list of 10 integers can never go to 11. But where in the above code is the size of the array defined? It is not. The code simply declares the array; I must also make sure I create the actual instance of the array with a specified size.
To do this, I use the new operator, in a similar manner as I did in calling the constructor of an object. In the object’s case, I am saying “Make a new Car” or “Make a new Zoog.” With an array, I am saying “Make a new array of integers,” or “Make a new array of Car objects,” and so on. See array declaration in Figure 9-4.
The array declaration in Figure 9-4 allows me to specify the array size: how many elements I want the array to hold (or, technically, how much memory in the computer I am asking for to store my beloved data). I write this statement as follows: the new operator, followed by the data type, followed by the size of the array enclosed in brackets. This size must be an integer. It can be a hard-coded number, a variable (of type integer), or an expression that evaluates to an integer (like 2 + 2).
Things are looking up. Not only did I successfully declare the existence of an array, but I have given it a size and allocated physical memory for the stored data. A major piece is missing, however: the data stored in the array itself!
One way to fill an array is to hard-code the values stored in each spot of the array.
As you can see, each element of the array is referred to individually by specifying an index, starting at 0. The syntax for this is the name of the array, followed by the index value enclosed in brackets.
arrayName[INDEX]
A second option for initializing an array is to manually type out a list of values enclosed in curly braces and separated by commas.
Both of these approaches are not commonly used and you will not see them in most of the examples throughout the book. In fact, neither initialization method has really solved the problem posed at the beginning of the chapter. Imagine initializing each element individually with a list of 100 or (gasp) 1,000 or (gasp gasp!) 1,000,000 elements.
The solution to all of your woes involves a means for iterating through the elements of the array. Ding ding ding. Hopefully a loud bell is ringing in your head. Loops! (If you’re lost, revisit Chapter 6.)
Consider, for a moment, the following problem:
1. Create an array of 1,000 floating point numbers.
2. Initialize every element of that array with a random number between 0 and 10.
Part 1 you already know how to do.
float[] values = new float[1000];
What I want to avoid is having to do this for Part 2:
values[0] = random(0, 10);
values[1] = random(0, 10);
values[2] = random(0, 10);
values[3] = random(0, 10);
values[4] = random(0, 10);
values[5] = random(0, 10);
// etc. etc.
Let’s describe in English what I want to program.
For every number n from 0 to 999, initialize the nth element stored in array as a random value between 0 and 10. Translating into code, I have:
values[n] = random(0, 10);
values[n + 1] = random(0, 10);
values[n + 2] = random(0, 10);
values[n + 3] = random(0, 10);
values[n + 4] = random(0, 10);
values[n + 5] = random(0, 10);
Unfortunately, the situation has not improved. I have, nonetheless, taken a big leap forward. By using a variable (n) to describe an index in the array, I can now employ a while loop to initialize every n element.
A for loop allows you to be even more concise, as Example 9-5 shows.
What was once 1,000 lines of code is now three!
I can exploit the same technique for any type of array operation I might like to do beyond simply initializing the elements. For example, I could take the array and double the value of each element. (I will use i from now on instead of n as it is more commonly used by programmers.)
There is one problem with Example 9-6: the use of the hard-coded value 1,000. Striving to be better programmers, you should always question the existence of a hard-coded number. In this case, what if you wanted to change the array to have 2,000 elements? If your program was very long with many array operations, you would have to make this change everywhere throughout your code. Fortunately, Processing offers a nice means for accessing the size of an array dynamically, using the dot syntax you learned for objects in Chapter 8. length is a property of every array and you can access it by saying:
arrayName dot length
Let’s use length while clearing an array. This will involve resetting every value to 0.
A seemingly trivial task, programming a trail following the mouse, is not as easy as it might initially appear. The solution requires an array, which will serve to store the history of mouse locations. I will use two arrays, one to store horizontal mouse locations, and one for vertical. Let’s say, arbitrarily, that I want to store the last 50 mouse locations.
First, I declare the two arrays.
int[] xpos = new int[50];
int[] ypos = new int[50];
Second, in setup(), I must initialize the arrays. Since at the start of the program there has not been any mouse movement, I will just fill the arrays with 0’s.
for (int i = 0; i < xpos.length; i++) {
xpos[i] = 0;
ypos[i] = 0;
}
Each time through the main draw() loop, I want to update the array with the current mouse location. Let’s choose to put the current mouse location in the last spot of the array. The length of the array is 50, meaning index values range from 0−49. The last spot is index 49, or the length of the array minus one.
Now comes the hard part. I want to keep only the last 50 mouse locations. By storing the current mouse location at the end of the array, I am overwriting what was previously stored there. If the mouse is at (10,10) during one frame and (15,15) during another, I want to put (10,10) in the second to last spot and (15,15) in the last spot. A solution is to shift all of the elements of the array down one spot before updating the current location. This is shown in Figure 9-5.
Element index 49 moves into spot 48, 48 moves into spot 47, 47 into 46, and so on. I can do this by looping through the array and setting each element index i to the value of element i+1. Note I must stop at the second to last value since for element 49 there is no element 50 (49 plus 1). In other words, instead of having an exit condition
i < xpos.length;
I must instead say:
i < xpos.length − 1;
The full code for performing this array shift is as follows:
for (int i = 0; i < xpos.length − 1; i++) {
xpos[i] = xpos[i + 1];
ypos[i] = ypos[i + 1];
}
Finally, I can use the history of mouse locations to draw a series of circles. For each element of the xpos array and ypos array, draw an ellipse at the corresponding values stored in the array.
for (int i = 0; i < xpos.length; i++) {
stroke(0);
fill(175);
ellipse(xpos[i], ypos[i], 32, 32);
}
Making this a bit fancier, you might choose to link the brightness of the circle as well as the size of the circle to the location in the array, that is, the earlier (and therefore older) values will be bright and small and the later (newer) values will be darker and bigger. This is accomplished by using the counting variable i to evaluate color and size.
for (int i = 0; i < xpos.length; i++) {
noStroke();
fill(255 − i * 5);
ellipse(xpos[i], ypos[i], i, i);
}
Putting all of the code together, I have the following example, with the output shown in Figure 9-6.
Exercise 9-7: Rewrite the snake example in an object-oriented fashion with a Snake class. Can you make snakes with slightly different looks (different shapes, colors, sizes)? (For an advanced problem, create a Point class that stores an x and y coordinate as part of the sketch. Each snake object will have an array of Point objects, instead of two separate arrays of x and y values. This involves arrays of objects, covered in the next section.)
I know, I know. I still have not fully answered the question. How can you write a program with 100 car objects?
One of the nicest features of combining object-oriented programming with arrays is the simplicity of transitioning a program from one object to 10 objects to 10,000 objects. In fact, if I have been careful, I will not have to change the Car class whatsoever. A class does not care how many objects are made from it. So, assuming I keep the identical Car class code, let’s look at how to expand the main program to use an array of objects instead of just one.
Let’s revisit the main program for one Car object.
Car myCar;
void setup() {
myCar = new Car(color(255, 0, 0), 0, 100, 2);
}
void draw() {
background(255);
myCar.move();
myCar.display();
}
There are three steps in the above code and each one needs to be changed to account for an array.
This leaves you with Example 9-9. Note how changing the number of cars present in the program requires only altering the array definition. Nothing else anywhere has to change!
When you first learned about variables (Chapter 4) and conditionals (Chapter 5), you programmed a simple rollover effect. A rectangle appears in the window and is one color when the mouse is on top and another color when the mouse is not. The following is an example that takes this simple idea and puts it into a Stripe class. Even though there are 10 stripes, each one individually responds to the mouse by having its own rollover() function.
void rollover(int mx, int my) {
if (mx > x & & mx < x + w) {
mouse = true;
} else {
mouse = false;
}
}
This function checks to see if a point (mx,my) is contained within the vertical stripe. Is it greater than the left edge and less than the right edge? If so, a boolean variable mouse is set to true. When designing your classes, it’s often convenient to use a boolean variable to keep track of properties of an object that resemble a switch. For example, a Car object could be running or not running. Zoog could be happy or not happy.
This boolean variable is used in a conditional statement inside of the Stripe object’s display() function to determine the stripe’s color.
void display() {
if (mouse) {
fill(255);
} else {
fill(255, 100);
}
noStroke();
rect(x, 0, w, height);
}
When I call the rollover() function on that object, I can then pass in mouseX and mouseY as arguments.
stripes[i].rollover(mouseX, mouseY);
Even though I could have accessed mouseX and mouseY directly inside of the rollover() function, it’s better to use arguments. This allows for greater flexibility. The Stripe object can check and determine if any (x,y) coordinate is contained within its rectangle. Perhaps later, I will want the stripe to turn white when another object, rather than the mouse, is over it.
Here is the full “interactive stripes” example.
OK, so I have a confession to make. I lied. Well, sort of. See, earlier in this chapter, I made a very big point of emphasizing that once you set the size of an array, you can never change that size. Once you have made 10 Button objects, you can’t make an 11th.
And I stand by those statements. Technically speaking, when you allocate 10 spots in an array, you have told Processing exactly how much space in memory you intend to use. You can’t expect that block of memory to happen to have more space next to it so that you can expand the size of your array.
However, there is no reason why you couldn’t just make a new array (one that has 11 spots in it), copy the first 10 from your original array, and pop a new Button object in the last spot. Processing, in fact, offers a set of array functions that manipulate the size of an array by managing this process for you. They are: shorten(), concat(), subset(), append(), splice(), and expand(). In addition, there are functions for changing the order in an array, such as sort() and reverse().
Details about all of these functions can be found in the reference. Let’s look at one example that uses append() to expand the size of an array. This example (which includes an answer to Exercise 8-5 on page 154) starts with an array of one object. Each time the mouse is pressed, a new object is created and appended to the end of the original array.
Another means of having a resizable array is through the use of a special object known as an ArrayList, which will be covered in Chapter 23.
It’s time to complete Zoog’s journey and look at how to move from one Zoog to many. In the same way that I generated the Car array or Stripe array example, I can simply copy the exact Zoog class created in Example 8-3 and implement an array.
3.144.238.20