Day 5

Arrays, Conditionals, and Loops

by Laura Lemay


CONTENTS

Although you could write Java programs using what you've learned so far, those programs would be pretty dull. Much of the good stuff in Java or in any programming language results when you have arrays to store values in and control-flow constructs (loops and conditionals) to execute different bits of a program based on tests. Today, you'll find out about the following:

Arrays

Arrays in Java, as in other languages, are a way to store collections of items into a single unit. The array has some number of slots, each of which holds an individual item. You can add and delete items to those slots as needed. Unlike in other languages, however, arrays in Java are actual objects that can be passed around and treated just like other objects.

New Time
An array is a collection of items. Each slot in the array can hold an object or a primitive value. Arrays in Java are objects that can be treated just like other objects in the language.

Arrays can contain any type of element value (primitive types or objects), but you can't store different types in a single array. You can have an array of integers or an array of strings or an array of arrays, but you can't have an array that contains, for example, both strings and integers.

To create an array in Java, you use three steps:

  1. Declare a variable to hold the array.
  2. Create a new array object and assign it to the array variable.
  3. Store things in that array.

Declaring Array Variables

The first step in creating an array is creating a variable that will hold the array, just as you would any other variable. Array variables indicate the type of object the array will hold (just as they do for any variable) and the name of the array, followed by empty brackets ([]). The following are all typical array variable declarations:

String difficultWords[];

Point hits[];

int temps[];

An alternate method of defining an array variable is to put the brackets after the type instead of after the variable. They are equivalent, but this latter form is often much more readable. So, for example, these three declarations could be written like this:

String[] difficultWords;

Point[] hits;

int[] temps;

Creating Array Objects

The second step is to create an array object and assign it to that variable. There are two ways to do this:

The first way is to use the new operator to create a new instance of an array:

String[] names = new String[10];

That line creates a new array of Strings with 10 slots (sometimes called elements). When you create a new array object using new, you must indicate how many slots that array will hold. This line does not put actual String objects in the slots-you'll have to do that later.

Array objects can contain primitive types such as integers or booleans, just as they can contain objects:

int[] temps = new int[99];

When you create an array object using new, all its slots are initialized for you (0 for numeric arrays, false for boolean, '\0' for character arrays, and null for objects). You can then assign actual values or objects to the slots in that array. You can also create an array and initialize its contents at the same time. Instead of using new to create the new array object, enclose the elements of the array inside braces, separated by commas:

String[] chiles = { "jalapeno", "anaheim", "serrano",
    "habanero", "thai" };

Technical Note
Note that the Java keyword null refers to a null object (and can be used for any object reference). It is not equivalent to zero or the '\0' character as the NULL constant is in C.

Each of the elements inside the braces must be of the same type and must be the same type as the variable that holds that array (the Java compiler will complain if they're not). An array the size of the number of elements you've included will be automatically created for you. This example creates an array of String objects named chiles that contains five elements.

Accessing Array Elements

Once you have an array with initial values, you can test and change the values in each slot of that array. To get at a value stored within an array, use the array subscript expression ([]):

myArray[subscript];

The myArray part of this expression is a variable holding an array object, although it can also be an expression that results in an array. The subscript part of the expression, inside the brackets, specifies the number of the slot within the array to access. Array subscripts start with 0, as they do in C and C++. So, an array with 10 elements has 10 array slots accessed using subscript 0 to 9.

Note that all array subscripts are checked when your Java program is run to make sure that they are inside the boundaries of the array (greater than or equal to 0 but less than the array's length). Unlike in C, it is impossible in Java to access or assign a value to an array slot outside the boundaries of the array (thereby avoiding a lot of the common problems and bugs that result from overrunning the bounds of an array in C-like languages). Note the following two statements, for example:

String[] arr = new String[10];
arr[10] = "eggplant";

A program with that last statement in it produces an error at that line when you try to run it. (Actually, to be more technically correct, it throws an exception. You'll learn more about exceptions on Day 18, "Multithreading.") The array stored in arr has only 10 slots numbered from 0, the element at subscript 10 doesn't exist.

If the array subscript is calculated at runtime (for example, as part of a loop) and ends up outside the boundaries of the array, the Java interpreter also produces an error.

How can you keep from accidentally overrunning the end of an array in your own programs? You can test for the length of the array in your programs using the length instance variable-it's available for all array objects, regardless of type:

int len = arr.length // returns 10

However, just to reiterate: The length of the array is 10, but its subscript can only go up to 9. Arrays start numbering from 0. Whenever you work with arrays, keep this in mind and subtract 1 from the length of the array to get its largest element.

Changing Array Elements

To assign an element value to a particular array slot, merely put an assignment statement after the array access expression:

myarray[1] = 15;
sentence[0] = "The";
sentence[10] = sentence[0];

An important thing to note is that an array of objects in Java is an array of references to those objects (similar in some ways to an array of pointers in C or C++). When you assign a value to a slot in an array, you're creating a reference to that object, just as you do for a plain variable. When you move values around inside arrays (as in that last line), you just reassign the reference; you don't copy the value from one slot to another. Arrays of primitive types such as ints or floats do copy the values from one slot to another.

Arrays of references to objects, as opposed to the objects themselves, are particularly useful because you can have multiple references to the same objects both inside and outside arrays. For example, you can assign an object contained in an array to a variable and refer to that same object by using either the variable or the array position.

Got it? Arrays are pretty simple to create and modify, but they provide an enormous amount of functionality for Java. You'll find yourself running into arrays a lot the more you use Java.

To finish up the discussion on arrays, here's a simple program that shows how to create, initialize, modify, and examine parts of an array. Listing 5.1 has the code.


Listing 5.1. Various simple array operations.
 1: class ArrayTest {
 2:
 3:    String[] firstNames = { "Dennis", "Grace", "Bjarne", "James" };
 4:    String[] lastNames = new String[firstNames.length];
 5:
 6:    void printNames() {
 7:      int i = 0;
 8:       System.out.println(firstNames[i]
 9:          + " " + lastNames[i]);
10:      i++;
11:       System.out.println(firstNames[i]
12:         + " " + lastNames[i]);
13:       i++;
14:       System.out.println(firstNames[i]
15:          + " " + lastNames[i]);
16:       i++;
17:      System.out.println(firstNames[i]
18:          + " " + lastNames[i]);
19:    }
20:
21:    public static void main (String args[]) {
22:      ArrayTest a = new ArrayTest();
23:       a.printNames();
24:       System.out.println("----------");
25:       a.lastNames[0] = "Ritchie";
26:       a.lastNames[1] = "Hopper";
27:      a.lastNames[2] = "Stroustrup";
28:       a.lastNames[3] = "Gosling";
29:       a.printNames();
30:   }
31:}

Dennis null
Grace null
Bjarne null
James null
----------
Dennis Ritchie
Grace Hopper
Bjarne Stroustrup
James Gosling

Analysis
This somewhat verbose example shows you how to create and use arrays. The class we've created here, ArrayTest, has two instance variables that hold arrays of String objects. The first, called firstNames, is declared and initialized in the same line (line 3) to contain four strings. The second instance variable, lastNames, is declared and created in line 4, but no initial values are placed in the slots. Note also that we created the lastNames array to have exactly the same number of slots as the firstNames array by using the firstNames.length variable as the initial array index. The length instance variable on array objects returns the number of slots in the array.

The ArrayTest class also has two methods: printNames() and main(). printNames(), defined in lines 6 through 19, is a utility method that does nothing but go through the firstNames and lastNames arrays sequentially, printing the values of each slot, one name per line. Note that the array index we've defined here (i) is initially set to 0 because Java array slots all start numbering from 0.

Finally, there is main(), which performs the actual actions of this example. The main() method here does four things:

Note
Who are the people in this example? They're inventors of computer programming languages. Dennis Ritchie is the inventor of C, Bjarne Stroustrup did C++, Grace Hopper is credited with COBOL, and, finally, James Gosling is the principal designer of Java.

One other note I should make about Listing 5.1 is that it's a terrible example of programming style. Usually when you deal with arrays you do not hard code the number of elements into the code as we have here; instead you use a loop to go through each element of the array in turn. This makes the code a lot shorter and, in many cases, easier to read. You'll learn about loops later in this section, and we'll rewrite this example so that it works more flexibly.

Multidimensional Arrays

One last thing to note about arrays before we move on to the rest of this lesson is about multidimensional arrays. Java does not directly support multidimensional arrays. However, you can declare and create an array of arrays (and those arrays can contain arrays, and so on, for however many dimensions you need) and access the arrays as you would C-style multidimensional arrays:

int coords[][] = new int[12][12];
coords[0][0] = 1;
coords[0][1] = 2;

Block Statements

Before we launch into the last two-thirds of this lesson, let's take a small detour into a topic I haven't mentioned a whole lot up to this point (but that will be important later on): block statements.

A block statement is simply a group of Java statements surrounded by braces ({}). You've seen blocks a whole lot already; you've used a block statement to contain the variables and methods in a class definition, and inside that block you've also used blocks to hold the body of a method definition. The opening brace opens the block, and the closing brace closes the nearest closing block. Easy, right?

You can also use blocks even further, inside method definitions. The rule is that you can use a block anywhere a single statement would go. Each statement inside the block is then executed sequentially.

New Term
A block statement is a group of individual Java statements enclosed in braces ({}). You can put a block statement anywhere a single statement can go.

So what's the difference between using a group of individual statements and using a block? The block creates a new local variable scope for the statements inside it. This means that you can declare and use local variables inside a block, and those variables will cease to exist after the block is finished executing. For example, here's a block inside a method definition that declares a new variable y. You cannot use y outside the block in which it's declared:

void testblock() {
    int x = 10;
    { // start of block
      int y = 50;
      System.out.println("inside the block:");
      System.out.println("x:" + x);
      System.out.println("y:" + y);
    } // end of block
}

Blocks are not usually used in this way-alone in a method definition, with random variable declarations inside them. You've mostly seen blocks up to this point surrounding class and method definitions, but another very common use of block statements is in the control flow constructs you'll learn about in the remainder of today's lesson.

if Conditionals

The if conditional statement is used when you want to execute different bits of code based on a simple test. if conditions are nearly identical to if statements in C: They contain the keyword if, followed by a boolean test, followed by either a single statement or a block statement to execute if the test is true. Here's a simple example that prints the message x is smaller than y only if the value of x is less than the value of y:

if (x < y)
    System.out.println("x is smaller than y");

An optional else keyword provides the alternative statement to execute if the test is false:

if (x < y)
    System.out.println("x is smaller than y");
else System.out.println("y is bigger");

New Term
The if conditional executes different bits of code based on the result of a single boolean test.

Technical Note
The difference between if conditionals in Java and C or C++ is that the test must return a boolean value (true or false). Unlike in C, the test cannot return an integer.

Using if, you can only include a single statement as the code to execute after the test (in this case, the System.out.println() method for each one). But because a block can appear anywhere a single statement can, if you want to do more than just one thing (as you usually will), you can enclose those statements inside a block:

if (engineState == true )
    System.out.println("Engine is already on.");
else {
    System.out.println("Now starting Engine.");
    if (gasLevel >= 1)
        engineState = true;
    else System.out.println("Low on gas! Can't start engine.");
}

This example uses the test (engineState == true). For boolean tests of this type, a common shortcut is merely to include the first part of the expression rather than explicitly test its value against true or false. Because it's a boolean variable, it automatically returns true or false all by itself, so you don't have to explicitly test it for that value. Here's a shorter version of the previous code, with the test replaced with the shorthand version:

if (engineState)
    System.out.println("Engine is on.");
else System.out.println("Engine is off.");

Listing 5.2 shows another simple example-this one in full application form. The Peeper class contains one utility method called peepMe(), which tests a value to see if it's even. If it is, it prints Peep! to the screen.


Listing 5.2. The Peeper class.
 1: class Peeper {
 2:
 3:    void peepMe(int val) {
 4:       System.out.println("Value is "
 5:          + val + ". ");
 6:       if (val % 2 == 0)
 7:         System.out.println("Peep!");
 8:    }
 9:
10:   public static void main (String args[]) {
11:      Peeper p = new Peeper();
12:
13:       p.peepMe(1);
14:       p.peepMe(2);
15:        p.peepMe(54);
16:       p.peepMe(77);
17:      p.peepMe(1346);
18:    }
19: }

Value is 1.
Value is 2.
Peep!
Value is 54.
Peep!
Value is 77.
Value is 1346.
Peep!

Analysis
The heart of the Peeper class is the peepMe() method (lines 3 through 8), where values are tested and an appropriate message is printed. Unlike the methods you've defined in previous examples, note that the definition of peepMe() includes a single integer argument (see line 3). The peepMe() method starts by printing out the value that was passed to it. Then that argument is tested, using an if conditional, to see if it's an even number. (The modulus test, as you'll remember from Day 3, "Java Basics," returns the remainder of the division of its operands. So if the remainder of a number divided by 2 is 0, it's an even number.) If the number is even, Peep! is printed (you'll learn more about defining methods with arguments tomorrow).

We'll use a main() method, as always, in this application to create a new instance of Peeper and test it, calling the peepMe() method repeatedly with different values. In the output, only the values that are even get a Peep! message.

The Conditional Operator

An alternative to using the if and else keywords in a conditional statement is to use the conditional operator, sometimes called the ternary operator (ternary means three; the conditional operator has three parts).

The conditional operator is an expression, meaning that it returns a value (unlike the more general if, which can only result in a statement or block being executed). The conditional operator is most useful for very short or simple conditionals and looks like this:

test ? trueresult : falseresult;

test is a boolean expression that returns true or false, just like the test in the if statement. If the test is true, the conditional operator returns the value of trueresult; if it's false, it returns the value of falseresult. For example, the following conditional tests the values of x and y, returns the smaller of the two, and assigns that value to the variable smaller:

int smaller = x < y ? x : y;

The conditional operator has a very low precedence; that is, it's usually evaluated only after all its subexpressions are evaluated. The only operators lower in precedence are the assign-ment operators. See the precedence chart in Day 3's lesson for a refresher on precedence of all the operators.

switch Conditionals

A common programming practice in any language is to test a variable against some value, and if it doesn't match that value, to test it again against a different value, and if it doesn't match that one to make yet another test, and so on until it matches with the right result. Using only if statements, this can become unwieldy, depending on how it's formatted and how many different options you have to test. For example, you might end up with a set of if statements something like this or longer:

if (oper == '+')
  addargs(arg1, arg2);
else if (oper == '-')
   subargs(arg1, arg2);
else if (oper == '*')
   multargs(arg1, arg2);
else if (oper == '/')
   divargs(arg1, arg2);

This form of if statement is called a nested if because each else statement in turn contains yet another if, and so on, until all possible tests have been made.

Many languages have a shorthand version of the nested if that is (somewhat) easier to read and allows you to group the tests and actions. Called a switch or case statement, in Java it's called switch and behaves as it does in C:

switch (test) {
    case valueOne:
      resultOne;
      break;
    case valueTwo:
      resultTwo;
      break;
    case valueThree:
      resultThree;
      break;
    ...
    default: defaultresult;
}

In the switch statement, the test (a variable or expression that evaluates to a byte, char, short, or int) is compared with each of the case values (valueOne, valueTwo, and so on) in turn. If a match is found, the statement (or statements) after the test is executed. If no match is found, the default statement is executed. The default is optional, so if there isn't a match in any of the cases and default doesn't exist, the switch statement completes without doing anything.

Note that the significant limitation of the switch in Java is that the tests and values can be only simple primitive types (and then only primitive types that are automatically castable to int). You cannot use larger primitive types (long, float), strings, or other objects within a switch, nor can you test for any relationship other than simple equality. This limits the usefulness of switch; nested ifs can work for any kind of test on any type.

Here's a simple example of a switch statement similar to the nested if shown earlier:

switch (oper) {
    case '+':
        addargs(arg1, arg2);
        break;
    case '-':
        subargs(arg1, arg2);
        break;
    case '*':
        multargs(arg1, arg2);
        break;
    case '/':
        divargs(arg1, arg2);
        break;
 }

There are two things to be aware of in this example: The first is that after each case, you can include a single result statement or as many as you need. Unlike with if, you don't need to surround multiple statements with braces for it to work. The second thing to note about this example is the break statement included at the end of every case. Without the explicit break, once a match is made, the statements for that match (and also all the statements further down in the switch for all the other cases) are executed until a break or the end of the switch is found. In some cases, this may be exactly what you want to do, but in most cases, you'll want to make sure to include the break so that only the statements you want to be executed are actually executed (break, which you'll learn about in the section "Breaking Out of Loops," stops execution at the current point and jumps to the code outside of the next closing bracket (})).

One handy use of allowing a switch to continue processing statements after a match is found occurs when you want multiple values to match to the same statements. In this instance, you can use multiple case lines with no result, and the switch will execute the first statement it finds. For example, in the following switch statement, the string "x is an even number." is printed if x has a value of 2, 4, 6, or 8. All other values of x print the string "x is an odd number.":

switch (x) {
    case 2:
    case 4:
    case 6:
    case 8:
       System.out.println("x is an even number.");
       break;
    default: System.out.println("x is an odd number.");
}

Listing 5.3 shows yet another example of a switch. This class, called NumberReader, converts integer values to their actual English word equivalents using a method called convertIt().


Listing 5.3. The NumberReader class.
 1: class NumberReader {
 2:
 3:    String convertNum(int val) {
 4:       switch (val) {
 5:          case 0: return "zero ";
 6:          case 1: return "one ";
 7:         case 2: return "two ";
 8:          case 3: return "three ";
 9:          case 4: return "four ";
10:         case 5: return "five ";
11:         case 6: return "six ";
12:         case 7: return "seven ";
13:          case 8: return "eight ";
14:          case 9: return "nine ";
15:          default: return " ";
16:       }
17:   }
18:
19:    public static void main (String args[]) {
20:      NumberReader n = new NumberReader();
21:      String num = n.convertNum(4) + n.convertNum(1)  + n.convertNum(5);
22:      System.out.println("415 converts to " + num);
23:   }
24:}

415 converts to four one five

Analysis
The heart of this example is, of course, the main switch statement in the middle of the convert Num ( ) method in lines 4 through 16. This switch statement takes the integer argument that was passed into convert Nm ( ) and, when it finds a match, returns the appropriate string value. (Note that this method is defined to return a string as opposed to the other methods you've defined up to this point, which didn't return anything. You'll learn more about this tomorrow.)

So where are the break statements? You don't need them here because you're using return instead. return is similar to break except that it breaks out of the entire method definition and returns a single value. Again, you'll learn more about this tomorrow when you learn all about how to define methods.

At this point you've probably seen enough main() methods to know what's going on, but let's run through this one quickly.

Line 20 creates a new instance of the NumberReader class.

Line 21 defines a string called num that will be the concatenation of the string values of three numbers. Each number is converted using a call to the convertNum() method.

Finally, line 22 prints out the result.

for Loops

The for loop, as in C, repeats a statement or block of statements until a condition is matched. for loops are frequently used for simple iterations in which you repeat a block of statements a certain number of times and then stop, but you can use for loops for just about any kind of loop.

The for loop in Java looks roughly like this:

for (initialization; test; increment) {
    statements;
}

The start of the for loop has three parts:

The statement part of the for loop is the statements that are executed each time the loop iterates. Just as with if, you can only include one statement, although a block will work just fine as well.

Remember the example in the section on arrays where I said that iterating over the contents of an array is usually done with a loop? Here's an example of a for loop that does just that-it initializes all the values of a String array to null strings:

String strArray[] = new String[10]; \\ the array
int i; // loop index

for (i = 0; i < strArray.length; i++)
    strArray[i] = "";

In this example, the variable I keeps track of the number of times the loop has occurred; it also makes a convenient index for the array itself. Here, we start the for loop with an index of I. The test for when the for loop will end is whether the current index is less than the length of the array (once the index is bigger than the array, you should stop), and the increment is simply to add 1 to the index each time. Then, for every loop you can put a null string ("") into the array at the given slot.

Any of the parts of the for loop can be empty statements; that is, you can simply include a semicolon with no expression or statement, and that part of the for loop will be ignored. Note that if you do use a null statement in your for loop, you may have to initialize or increment any loop variables or loop indices yourself elsewhere in the program.

You can also have an empty statement for the body of your for loop, if everything you want to do is in the first line of that loop. For example, here's one that finds the first prime number higher than 4000 (it calls a method called notPrime(), which will theoretically have a way of figuring that out):

for (i = 4001; notPrime(i); i += 2)
    ;

Note that a common mistake in C that also occurs in Java is to accidentally put a semicolon after the first line of the for loop:

for (i = 0; i < 10; i++);
    System.out.println("Loop!");

Because the first semicolon ends the loop with an empty statement, the loop doesn't actually do anything. The println() function will be printed only once because it's actually outside the for loop entirely. Be careful not to make this mistake in your own Java programs.

To finish up for loops, let's rewrite that example with the names from the array section. The original example is long and repetitive and only works with an array four elements long. This version, shown in Listing 5.4, is shorter and more flexible (but it returns the same output).


Listing 5.4. A modified array test with loops.
 1: class NamesLoop {
 2:
 3:    String[] firstNames = { "Dennis", "Grace", "Bjarne", "James" };
 4:    String[] lastNames = new String[firstNames.length];
 5:
 6:    void printNames() {
 7:      for (int i = 0; i < firstNames.length; i++)
 8:          System.out.println(firstNames[i] + " " + lastNames[i]);
 9:    }
10:
11:   public static void main (String args[]) {
12:      ArrayTest a = new ArrayTest();
13:       a.printNames();
14:       System.out.println("----------");
15:       a.lastNames[0] = "Ritchie";
16:       a.lastNames[1] = "Hopper";
17:      a.lastNames[2] = "Stroustrup";
18:       a.lastNames[3] = "Gosling";
19:
20:      a.printNames();
21:}
22:}

Dennis null
Grace null
Bjarne null
James null
----------
Dennis Ritchie
Grace Hopper
Bjarne Stroustrup
James Gosling

Analysis
The only difference between this example and Listing 5.1 is in the printNames() method. Instead of going through the array slots one by one, this example uses a for loop to iterate through the array one slot at a time, stopping at the last element in the array. Using a more general-purpose loop to iterate over an array allows you to use printNames() for any array of any size and still have it print all the elements.

while and do Loops

Finally, there are while and do loops. while and do loops, like for loops, repeat the execution of a block of Java code until a specific condition is met. Whether you use a for loop, a while, or a do is mostly a matter of your programming style.

while and do loops are exactly the same as in C and C++ except that their test conditions must be booleans.

while Loops

The while loop is used to repeat a statement or block of statements as long as a particular condition is true. while loops look like this:

while (condition) {
    bodyOfLoop;
}

The condition is a boolean test because it is in the if and for constructions. If the test returns true, the while loop executes the statements in bodyOfLoop and then tests the condition again, repeating until the condition is false. I've shown the while loop here with a block statement because it's most commonly used, although you can use a single statement in place of the block.

Listing 5.5 shows an example of a while loop that copies the elements of an array of integers (in array1) to an array of floats (in array2), casting each element to a float as it goes. The one catch is that if any of the elements in the first array is 0, the loop will immediately exit at that point.


Listing 5.5. while loops to copy array elements.
 1: class CopyArrayWhile {
 2:   public static void main (String args[]) {
 3:       int[] array1 = { 5, 7, 3, 6, 0, 3, 2, 1 };
 4:       float[] array2 = new float[array1.length];
 5:
 6:        System.out.print("array1: [ ");
 7:       for (int i = 0; i < array1.length; i++) {
 8:          System.out.print(array1[i] + " ");
 9:        }
10:       System.out.println("]");
11:
12:       System.out.print("array2: [ ");
13:       int count = 0;
14:       while ( count < array1.length && array1[count] != 0) {
15:              array2[count] = (float) array1[count];
16:              System.out.print(array2[count++] + " ");
17:      }
18:        System.out.println("]");
19:    }
20:}

array1: [ 5 7 3 6 0 3 2 1 ]
array2: [ 5 7 3 6 ]

I've done all the work here in main() to make things shorter. Here's what's going on here:

Lines 3 and 4, declare the arrays; array1 is an array of ints, which I've initialized to some suitable numbers. array2, or floats, is the same length as array1, but doesn't have any initial values.

Lines 6 through 10 are for output purposes; they simply iterate through array1 using a for loop to print out its values.

Lines 13 through 17 are where the interesting stuff happens. This bunch of statements both assigns the values of array2 (converting the numbers to floats along the array) and prints it out at the same time. We start with a count variable, which keeps track of the array index elements. The test in the while loop keeps track of the two conditions for existing the loop, where those two conditions are running out of elements in array1 or encountering a 0 in array1 (remember, that was part of the original description of what this program does). We can use the logical conditional && to keep track of the test; remember that && makes sure both conditions are true before the entire expression is true. If either one is false, the expression returns false and the loop exits.

So what goes on in this particular example? The output shows that the first four elements in array1 were copied to array2, but there was a 0 in the middle that stopped the loop from going any further. Without the 0, array2 should end up with all the same elements as array1.

Note that if the while loop's test is initially false the first time it is tested (for example, if the first element in that first array is 0), the body of the while loop will never be executed. If you need to execute the loop at least once, you can do one of two things:

do...while Loops

The do loop is just like a while loop, except that do executes a given statement or block until the condition is false. The main difference is that while loops test the condition before looping, making it possible that the body of the loop will never execute if the condition is false the first time it's tested. do loops run the body of the loop at least once before testing the condition. do loops look like this:

do {
    bodyOfLoop;
} while (condition);

Here, the bodyOfLoop part is the statements that are executed with each iteration. It's shown here with a block statement because it's most commonly used that way, but you can substitute the braces for a single statement as you can with the other control-flow constructs. The condition is a boolean test. If it returns true, the loop is run again. If it returns false, the loop exits. Keep in mind that with do loops, the body of the loop executes at least once.

Listing 5.6 shows a simple example of a do loop that prints a message each time the loop iterates (10 times, for this example):


Listing 5.6. A simple do loop.
 1: class DoTest {
 2:    public static void main (String args[]) {
 3:      int x = 1;
 4:
 5:      do {
 6:        System.out.println("Looping, round " + x);
 7:        x++;
 8:      } while (x <= 10);
 9:    }
10: }

Looping, round 1
Looping, round 2
Looping, round 3
Looping, round 4
Looping, round 5
Looping, round 6
Looping, round 7
Looping, round 8
Looping, round 9
Looping, round 10

Breaking Out of Loops

In all the loops (for, while, and do), the loop ends when the condition you're testing for is met. What happens if something odd occurs within the body of the loop and you want to exit the loop early? For that, you can use the break and continue keywords.

You've already seen break as part of the switch statement; it stops execution of the switch, and the program continues. The break keyword, when used with a loop, does the same thing-it immediately halts execution of the current loop. If you've nested loops within loops, execution picks up in the next outer loop; otherwise, the program merely continues executing the next statement after the loop.

For example, take that while loop that copied elements from an integer array into an array of floats until the end of the array or until a 0 is reached. You can instead test for that latter case inside the body of the while and then use a break to exit the loop:

int count = 0;
while (count < array1.length) {
    if (array1[count] == 0) {
        break;
    }
    array2[count] = (float) array1[count++];
}

continue is similar to break except that instead of halting execution of the loop entirely, the loop starts over at the next iteration. For do and while loops, this means that the execution of the block starts over again; for for loops, the increment and test expressions are evaluated and then the block is executed. continue is useful when you want to special-case elements within a loop. With the previous example of copying one array to another, you can test for whether the current element is 0 and restart the loop if you find it so that the resulting array will never contain zero. Note that because you're skipping elements in the first array, you now have to keep track of two different array counters:

int count1 = 0;
int count2 = 0;
while (count < array1.length) {
    if (array1[count1] == 0)  {
       continue;
        count1++
    }
    array2[count2++] = (float)array1[count1++];
}

Labeled Loops

Both break and continue can have an optional label that tells Java where to break to. Without a label, break jumps outside the nearest loop (to an enclosing loop or to the next statement outside the loop), and continue restarts the enclosing loop. Using labeled breaks and continues, you can break to specific points outside nested loops or continue a loop outside the current loop.

To use a labeled loop, add the label before the initial part of the loop, with a colon between them. Then, when you use break or continue, add the name of the label after the keyword itself:

out:
    for (int i = 0; i <10; i++) {
        while (x < 50) {
            if (i * x == 400)
                break out;
            ...
        }
        ...
    }

In this snippet of code, the label out labels the outer loop. Then, inside both the for and the while loops, when a particular condition is met, a break causes the execution to break out of both loops and continue executing any code after both loops.

Here's another example: The program shown in Listing 5.7 contains a nested for loop. Inside the innermost loop, if the summed values of the two counters is greater than 4, both loops exit at once.


Listing 5.7. A labeled loop example.
 1: class LabelTest {
 2:    public static void main (String arg[]) {
 3:
 4:      foo:
 5:      for (int i = 1; i <= 5; i++)
 6:        for (int j = 1; j <= 3; j++) {
 7:           System.out.println("i is " + i + ", j is " + j);
 8:           if (( i + j) > 4)
 9:           break foo;
10:        }
11:      System.out.println("end of loops");
12:    }
13:}

i is 1, j is 1
i is 1, j is 2
i is 1, j is 3
i is 2, j is 1
i is 2, j is 2
i is 2, j is 3
end of loops

As you can see, the loop iterated until the sum of i and j was greater than 4, and then both loops exited back to the outer block and the final message was printed.

Summary

Today you have learned about three main topics that you'll most likely use quite often in your own Java programs: arrays, conditionals, and loops.

You have learned how to declare an array variable, create and assign an array object to that variable, and access and change elements within that array.

Conditionals include the if and switch statements, with which you can branch to different parts of your program based on a boolean test.

Finally, you have learned about the for, while, and do loops, each of which enable you to execute a portion of your program repeatedly until a given condition is met.

Now that you've learned the small stuff, all that's left is to go over the bigger issues of declaring classes and creating methods within which instances of those classes can communicate with each other by calling methods. Get to bed early tonight, because tomorrow is going to be a wild ride.

Q&A

Q:
If arrays are objects, and you use new to create them, and they have an instance variable length, where is the Array class? I didn't see it in the Java class libraries.
A:
Arrays are implemented kind of weirdly in Java. The Array class is constructed automatically when your Java program runs; Array provides the basic framework for arrays, including the length variable. Additionally, each primitive type and object has an implicit subclass of Array that represents an array of that class or object. When you create a new array object, it may not have an actual class, but it behaves as if it does.
Q:
When you create an array, you have to give it the number of slots that the array has. What happens if you get halfway through your program and you've run out of slots in the array? Does the array get bigger automatically?
A:
No, arrays stay the same size throughout their existence. And, as I noted in the part of this lesson on arrays, you cannot access slots outside the bounds of the array, so adding extra elements to a full array will cause an error.

So what do you do if an array is full? You have to do it the hard way: Create a new array that's bigger than the initial one and copy all the elements from the old array to the new.

Optionally, you can use a data structure other than an array if you expect to have widely varying numbers of elements in the array. The Vector class, part of the java.util package, is a growable collection you can use in place of an array.

Q:
Does Java have gotos?
A:
The Java language defines the keyword goto, but it is not currently used for anything. In other words, no-Java does not have gotos.
Q:
I declared a variable inside a block statement for an if. When the if was done, the definition of that variable vanished. Where did it go?
A:
In technical terms, block statements form a new lexical scope. What this means is that if you declare a variable inside a block, it's only visible and usable inside that block. When the block finishes executing, all the variables you declared go away.

It's a good idea to declare most of your variables in the outermost block in which they'll be needed-usually at the top of a block statement. The exception might be very simple variables, such as index counters in for loops, where declaring them in the first line of the for loop is an easy shortcut.

Q:
Why can't you use switch with strings?
A:
Strings are objects, and switch in Java works only for the primitive types byte, char, short, and int. To compare strings, you have to use nested ifs, which enable more general expression tests, including string comparison.
Q:
It seems to me that a lot of for loops could be written as while loops, and vice versa.
A:
True. The for loop is actually a special case of while that enables you to iterate a loop a specific number of times. You could just as easily do this with a while and then increment a counter inside the loop. Either works equally well. This is mostly just a question of programming style and personal choice.