Monkeys at Keyboards: APE Antics
© Michael James Heron and Pauline Belford
Topic: Java Programming
Level: 1
Version: alpha

It usually takes more than three weeks to prepare a good impromptu speech.
Mark Twain

11 - Hooray for Arrays

PreviousTable of ContentsNext
Forum


Chapter Objectives

By the end of this chapter, the reader will be able to:



    11.1

    Introduction

    In previous chapters we've been looking at the skills we require in order to develop simple Java programs. We've looked at the fundamental building blocks of the language, namely repetition and selection structures. In this chapter, we're going to take a look at one of the more complex structures for representing data.

    Arrays are powerful tools for representing large collections of conceptually related sets of data, and represent the next plateau in programming development - once you have mastered these, whole new vistas of programs become possible to implement. It's one of the seminal moments in a young programmers life - when they master arrays. Brings a tear to my eye, so it does - so let's get on with it!

    11.2

    The Problem of Single Variables

    The variables we have used so far are individual units of data. They live a lonely, solitary life by themselves - unconnected and unrelated to even conceptually similar information. In real life however we must often deal with 'clumps' of related data. It is very useful for us to be able to refer to whole sets of data in a single statement. Luckily we can do this in Java through the medium of arrays.

    Arrays are conceptually-linked lists of data - we can use them to store however many separate pieces of data as we like (within limits). This is because arrays have different compartments into which information can be inserted. However, we can't just store any old kind of data in the compartments. Arrays are homogenous. What this means is that they have multiple compartments, but all the data that is placed in them must be of the same type.

    The benefit of arrays comes when we're dealing with many instances of the same kind of data. Consider a program that lets us store the ages of five people. To store five ages, we need to have five integer variables. We can use the Pacman interface to get the ages entered by the user.

    import draconia.APE.pacman.*; 

    public class AgesSingleVariables {

    public static void main (String [] args) {
    PacmanGame main = new PacmanGame();
    main.setMap ("blank");
    main.showGame();
    int age1;
    int age2;
    int age3;
    int age4;
    int age5;
    age1 = main.getIntFromUser ("Please enter the first age you want to store.");
    age2 = main.getIntFromUser ("Please enter the second age you want to store.");
    age3 = main.getIntFromUser ("Please enter the third age you want to store.");
    age4 = main.getIntFromUser ("Please enter the fourth age you want to store.");
    age5 = main.getIntFromUser ("Please enter the fifth age you want to store.");
    main.sendMessage ("The ages entered are: " + age1 + ", " + age2 + ", " + age3
    + ", " + age4 + ", " + age5);
    }

    }

    This is a workable solution - for five ages. What if we wanted to store ten, or twenty, or a hundred, or a thousand? You may well be wondering why we didn't use a for loop to get the ages. But if we were to do that then we would need to know which variable to store the age in, each time around the for loop. The only way we could do this would be by using if statements like so:

    import draconia.APE.pacman.*; 

    public class AgesIfStatements {

    public static void main (String [] args) {
    PacmanGame main = new PacmanGame();
    main.setMap ("blank");
    main.showGame();
    int age1;
    int age2;
    int age3;
    int age4;
    int age5;
    int counter;
    // counter to keep track of how many ages we have stored so far
    int totalNumAges;
    // number of ages we want to store
    totalNumAges = 5;
    age1 = 0;
    age2 = 0;
    age3 = 0;
    age4 = 0;
    age5 = 0;
    for (counter = 0; counter < totalNumAges; counter++) {
    if (counter == 0) {
    age1 = main.getIntFromUser ("Please enter the first age you want to store.");
    }

    else if (counter == 1) {
    age2 = main.getIntFromUser ("Please enter the second age you want to store.");
    }

    else if (counter == 2) {
    age3 = main.getIntFromUser ("Please enter the third age you want to store.");
    }

    else if (counter == 3) {
    age4 = main.getIntFromUser ("Please enter the fourth age you want to store.");
    }

    else if (counter == 4) {
    age5 = main.getIntFromUser ("Please enter the fifth age you want to store.");
    }

    main.sendMessage ("The ages entered are: " + age1 + ", " + age2 + ", " + age3
    + ", " + age4 + ", " + age5);
    }

    }

    }

    Obviously the code for this is not very extensible (it doesn't scale well) but this is only the beginning. The problem comes when we want to manipulate this data in order to get some meaningful information out of it. Consider if we want to find the average age. To do this we would add up all the ages and divide by the total number of ages:

    import draconia.APE.pacman.*; 

    public class AgesAverage {

    public static void main (String [] args) {
    PacmanGame main = new PacmanGame();
    main.setMap ("blank");
    main.showGame();
    int age1;
    int age2;
    int age3;
    int age4;
    int age5;
    int average;
    // variable to store the average age
    int numAges;
    // variable to store the number of ages stored in the program
    numAges = 5;
    age1 = main.getIntFromUser ("Please enter the first age you want to store.");
    age2 = main.getIntFromUser ("Please enter the second age you want to store.");
    age3 = main.getIntFromUser ("Please enter the third age you want to store.");
    age4 = main.getIntFromUser ("Please enter the fourth age you want to store.");
    age5 = main.getIntFromUser ("Please enter the fifth age you want to store.");
    average = (age1 + age2 + age3 + age4 + age5) / numAges;
    main.sendMessage ("The average age is: " + average);
    }

    }

    Again, this isn't a problem - for five ages. It's more of a problem for one hundred.

    What about if we wanted to find the highest age in the set of data? We would need a variable to keep track of the current oldest age. This should really be initialised to -1, in case the oldest person in the group is actually 0 years old. We would then need to check each age in turn against the current oldest age. If the age we are checking is greater than the current oldest age, then it becomes the current oldest age.

    int currentOldest; 
    // variable to store the oldest age found so far
    int currentOldest = -1;
    if (age1 > currentOldest) {
    currentOldest = age1;
    }

    if (age2 > currentOldest) {
    currentOldest = age2;
    }

    if (age1 > currentOldest) {
    currentOldest = age3;
    }

    if (age1 > currentOldest) {
    currentOldest = age4;
    }

    if (age1 > currentOldest) {
    currentOldest = age5;
    }

    main.sendMessage ("The oldest age is " + currentOldest);

    Imagine having to write that for a hundred ages - not only is it a nightmare to code with huge amounts of repetition, it's also crying out for a transcription error when you lose track of where you are and assign the wrong value to the wrong variable.

    11.3

    Introduction to Arrays

    Instead of writing such unwieldy code using single variables, we make use of arrays. Arrays are like filing cabinets with a set number of drawers, and we can put one piece of data into each of the drawers. The drawers are given numbers and we need to say which drawer number we need to access in order to get the data out.

    So, now that we've identified a need for arrays, how do we actually use them? Because arrays have multiple compartments, the syntax for declaring them is slightly different than that for declaring single variables.

    Arrays are assigned a single name - this is the name of the variable. Arrays are broken into a number of compartments, which are called elements. Each of these elements has an index, which is a numerical indication of in which compartment they are:

    Structure of Array
    Fig 11.1: Structure of Array

    11.4

    Declaring an Array

    When we create an array, we need to tell Java what size of array we want, so it knows how many compartments to create. We also need to tell Java what type of data we want to store in these compartments. Arrays can only store data of one type, so we do this for the array as a whole, not for the individual elements. The code for declaring the above array, which we have called myAges is:

    int [] myAges; 

    And that's it. The [] tell Java that we are declaring an array rather than a single integer variable.

    To declare an array of Strings to store names, for example, we would say:

    String[] myNames; 

    Note that when declaring the array we do not specify the size of the array. We do this when we initialise the array.

    Declaring the array simply sets aside some space in memory large enough to hold an array.

    11.5

    Initialising an Array

    As noted in the previous section, when declaring an array we specify the type of data that it is going to hold, and use the square brackets [] to indicate that we are creating an array of that type rather than a single variable. However, it is not until we initialise the array that we state what size of array we want.

    The code for initialising an array is different to that for initialising a single variable. Like the pacman game, arrays are actually objects. We will worry about what objects are in later chapters when we start writing our own classes, but at the moment all you need to know is that objects have methods associated with them, and that the method for declaring a new object requires use of the keyword new. Java has a template Array class, and when we create a new array we need to let Java know that we are creating a new object of this type. The code for initialising our myAges array so that it has 5 elements is:

    myAges = new int[5]; 

    We need to tell Java that it is a new array, what type of array it is, and within the square brackets we tell Java how many elements we need in the array.

    Similarly, to initialise our String array of names to hold 5 names we would say

    myNames = new String[5]; 

    When we initialise an integer array, as we did with myAges above, the values of all the elements are automatically initialised to 0. So at this stage the array looks like this:

    A freshly initialised array
    Fig 11.2: A freshly initialised array

    11.6

    Filling an Array

    Although declaring and initialising an array is far less time-consuming than it would be if we were using single variables - it is just as easy to create an array with 1000 elements as it is to create an array with 5 elements - we still need to set the values of each element separately.

    Java lets us fill the array with values in one of two ways. The first is to set the values of each element much the same way as we would set the value of a single variable. The only difference is that we must use put the number of the element whose value we are setting within square brackets like so:

    myAges [0] = 18; 
    myAges [1] = 20;
    myAges [2] = 25;
    myAges [3] = 22;
    myAges [4] = 11;

    At this point the array looks like this:

    A freshly initialised array
    Fig 11.3: A freshly initialised array

    The second way of filling the array allows us to fill the entire array in one (potentially very long) line of code. This method involves declaring and initialising the array in the same line of code. We declare the array and then tell it what values to store in each element in turn. From the length of the list we pass in, Java works out what size we want the array to be.

    int [] myAges = { 18, 20, 25, 22, 11 }; 

    11.7

    Accessing and Changing Values in an Array

    So, now we know how to declare and initialise an array. But what if we can't remember whether we put the correct age in element 2? We are bears of little brain after all - and once we've put something in an array our troubled minds may need to gain access to it later.

    To find out the value of a particular element of an array, we need to take the value out of the array and store it in a variable. So we declare a variable of the same type as the array type. We can reuse this variable later, so often this is called temp. It is a temporary variable used to store the value of a piece of data in the array, so it can be viewed or changed. Once we have declared our temporary variable, we then set it to be equal to the value of the element in the array that we wish to check:

    int temp; 
    temp = myAges [4];

    temp now holds the integer value currently stored in element 4 of the array, which is the number 11.

    We now discover that this was a mistype, and the person is actually 31. to change the value we simply overwrite the initial data entered like so:

    myAges [4] = 31; 

    11.8

    Manipulating Arrays Using For Loops

    So far, we have seen that arrays are easier to create than several individual variables, and marginally easier to populate. But we haven't got to the (hor)crux of why they are such fantastically wonderful things. One of the main benefits of using arrays is the ease of manipulation using for loops. Arrays and for loops make excellent bedfellows as we shall see.

    Remember way back to the first example of the problem of single variables? We were trying to find the average age and the highest age from the ages stored in the individual variables. We found it was unwieldy to do so for 5 ages, and would be a nightmare to attempt for 1000 ages. But using arrays and for loops, both these tasks are as easy to accomplish for 1000 ages as they are for 5! How!? - you cry. How can this be possible? Well, let's see.

    To find the highest age, we need a variable to store the current oldest age. We then need to this against each element in the array, updating the current oldest age whenever we come across an age that is older than it.

    Our array location numbers correspond to our for loop counter variable, and this makes it incredibly simple to step over each element in the array. We start our for loop counter at 0, and check the element at the current for loop counter location each time around the loop. We can then do what we need to do with that element. In the case of finding the highest age, this involves checking the age at that element against the current oldest age variable and updating or not updating our oldest age as appropriate.

    int currentOldest; 
    currentOldest = 0;
    int temp;
    for (int i = 0; i < 5; i++) {
    temp = myAges[i];
    if (temp > currentOldest) {
    currentOldest = temp;
    }

    main.sendMessage ("The oldest age stored is: " + currentOldest);
    }

    The first time around the loop, temp is set to the value stored in array element 0, which is 18. It checks if this is greater than currentOldest, which is currently set to 0. 18 is greater than 0, so currentOldest gets set to 18. We then send a message to the interface telling us the current value of currentOldest.

    The second time around the loop, currentOldest has the value 18. i has been incremented by 1 and is now 1, so temp is set to the value stored in the myAges array at element 1, which is 20. It then checks if temp is greater than currentOldest. 20 is greater than 18, so currentOldest gets set to 18, another message is sent to the screen, and the loop counter is incremented by 1 to become 2.

    The third time around the loop, currentOldest has the value 20. The counter, i, is now at 2, so temp is set to the value stored in element 2 of the array, which is 25. It then checks if temp (25) is greater than currentOldest (20) and finds that it is, so the code inside the if statement is executed and currentOldest is set to 25, and the loop counter is incremented to 3.

    The fourth time around the loop, currentOldest is 25. The counter, i, is now at 3, so temp is set to the value stored in element 3 of the array, which is 22. It then checks if temp (22) is greater than currentOldest (25) and finds that it is not. The program therefore does not execute the code within the if statement, currentOldest remains unchanged, and the loop counter is incremented to 4.

    The fifth time around the loop, currentOldest is 25. The counter, i, is now at 4, so temp is set to the value stored in element 4 of the array, which is 11. It then checks if temp (22) is greater than currentOldest (25) and finds that it is not. The program therefore does not execute the code within the if statement, currentOldest remains unchanged, and the loop counter is incremented to 5.

    Much simpler than for single variables. Not that in order to do this for an array of 1000 ages, all we need to change is the number of times the for loop executes:

    for (int i = 0; i < 1000; i++) { 
    }

    There is obviously still extra effort required to populate an array with 1000 ages, but once that is done the hard work is over and your for loops make light work of everything else for you.

    Working out how to calculate the average age in the array using a for loop is a task for the interested reader. Go on, give a try!

    11.9

    A Pacman Example

    We have previously prompted the user to enter information for pacman, when we looked at variables and when we looked at if statements. However, we only prompted the user for one command at a time and we only stored one command and executed it before asking them for another. Wouldn't it be good if we could prompt them to enter 10 commands, or 100, or however many were needed to solve a map, and then execute their whole list of instructions at once? Well, we can do this easily using an array to store the instructions.

    What we want to do is to create an array to store the required number of instructions. Let's say we want the user to enter the instructions to solve the bimble map. We can get the user to enter a number corresponding to a command like so:

    1. .move();
    2. .turnRight();
    3. .turnLeft();

    Firstly, we need to set up the map as usual.

    import draconia.APE.pacman.*; 

    public class ArrayCommands {

    public static void main (String [] args) {
    PacmanGame main = new PacmanGame();
    main.setMap ("bimble");
    main.showGame();

    We need an array to hold the list of instructions, and as we are going to be using numbers we will make this an array of integers. It takes 24 moves to get to the pill, so we will make the array size 24. We also need an integer variable to store the values entered into the input box by the user. This variable stores these temporarily and is reused, so it is common practice to call this temp.

    int temp; 
    int [] instructions;
    instructions = new int [24];

    Once we have initialised the array, we need to fill the array, which we do by prompting the user for commands, one at a time. To do this we can use a for loop to prompt for input, then fill the array at the next element in the array.

    Arrays have a handy method called .length, which tells us how long the array is. In our for loop we don't need to remember the size of the array, we can just use [arrayName].length to ensure the loop executes exactly as many times as there are elements in the array. We need a temp variable to store the current integer value entered by the user. We then store this in the next free element in the array. We do this by storing it in the location that the for loop counter is currently at.

    for (int i = 0; i < instructions.length; i++) { 
    temp = main.getIntFromUser ("Please enter the next command. 1 for move, 2 for " +
    "turn right, 3 for turn left");
    instructions[i] = temp;
    }

    The first time around the for loop, the array element 0 is filled, the second time around the value of the array element 1 is set, and so on until the for loop completes and the array is full.

    Once we have filled the array, we then need to process the instructions, one by one.

    We need a second for loop to do this. We loop over every element in the array, and this time we use the temp variable to take values out of the array to check them. We then check whether the value is 1, 2, 3, or something else. We use if statements to tell pacman what to do depending on the number of the instruction stored in the array.

    for (int i = 0; i < instructions.length; i++) { 
    temp = instructions[i];
    if (temp == 1) {
    main.move();
    }

    else if (temp == 2) {
    main.turnRight();
    }

    else if (temp == 3) {
    main.turnLeft();
    }

    else {
    main.sendMessage ("Command " + i + " was invalid");
    }

    11.10

    Conclusion

    So, that's the structure of an Array - it's a very powerful programming structure and one that opens up genuinely new kinds of programs to you. There are a number of these plateaus of knowledge in Programming - loops are one, selection structures are another, and arrays are the latest.

    We'll be seeing another of these defining moments when we talk about objects and classes - but for now, we should be satisfied with the new knowledge we have gained, and the phenomenal cosmic power that has become ours. We'll see much more of arrays in the coming chapters, you can be assured of that.

    Further Reading

    The following table details further reading on the topic in this chapter, and also any external resources that you may find useful.

    ResourceDescription
    Example Programs from this chapterThis is a zip file of all the programs shown in this chapter.

    PreviousTable of ContentsNext

    © 2004-2006 Michael James Heron and Pauline Belford