Lecture Notes 05: Intro to Loops and 1D Arrays
Objectives
By the end of this module, for simple HelloWorld-like
programs, you will be able to:
- Identify the new syntactic elements with the basic output-only for-loop.
- Demonstrate ability to mentally trace execution of for-loops.
- Produce desired output using for-loop and println's.
- Distinguish between count-up and count-down loops.
- Identify the the patterns that may allow the use of a nested for-loop.
Why is looping useful in computation?
- Being able to process a batch of survey results to do a study
- Searching through a list of webpages to find the ones that match a keyword
- Creating a countdown for a timer for a shopping cart
- Other examples?
Loops: the while-loop
Imagine we wanted to code up a countdown timer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 |
public class PrintCountdown
{
public static void main(String args[])
{
// Set up an initial count value
double count = 10;
// Now print and decrease it one by one
System.out.println(count--); // "--" acts AFTER we resolve the value, so it prints a 10
System.out.println(count--); // it prints a 9
System.out.println(count--); // it prints a 8
System.out.println(count--); // it prints a 7
System.out.println(count--); // it prints a 6
System.out.println(count--); // it prints a 5
System.out.println(count--); // it prints a 4
System.out.println(count--); // it prints a 3
System.out.println(count--); // it prints a 2
System.out.println(count--); // it prints a 1
System.out.println("Blastoff!"); // it prints Blastoff!
}
}
|
This is tedious...what if there were a way to represent repetition of the same code in Java? Above, we're
executing the identical line of code ten times...Let's see this same idea but with a new structure:
The while loop:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 |
public class AwesomePrintCountdown
{
public static void main(String args[])
{
// Set up an initial count value
double count = 10;
// Now print and decrease it one by one
while (count > 0)
{
System.out.println(count--);
}
System.out.println("Blastoff!"); // it prints Blastoff!
}
}
|
The new structure is:
- the reserved word while
- followed by a boolean condition in perenthesis, in this case "(count > 0)"
- follow by a set of statements inside a block surrounded by curly brackets, in this case the print with count--
The flow of execution for a while statement is:
- Evaluate the condition in parentheses, yielding true or false.
- If the condition is false, skip the following statements in braces.
- If the condition is true, execute the statements and go back to step 1.
Activity 1:
Let's Visualize this execution using our favourite visualizer,
Visualize AwesomePrintCountdown.java
Let's take a minute or two to modify the loop to count in the other direction. Note, that one of the most common mistakes
people make (myself included!) is forgetting to increment/decrement the counter.
If you end up doing this, you'll see what's called an infinite loop, where
the loop will go on forever (until you manually hard-exit the program -- this is generally bad).
An infinite loop, especially one that doesn't have a print statement, can look like the
computer is "hanging." If you notice this sort of thing, place a print statement in any
while loops you have, and it will immediately become obvious what's goig on.
Loops: the for-loop
While loops are great, but they're typically meant to be used when one doesn't know in
advance how many times the loop should be executed -- perhaps you want to keep prompting a user
to re-enter a password until they pick a valid one:
Java has an alternative to the while loop for cases when you know in advance how many times
you want the loop to execute: the for loop.
Consider this example:
Activity 2:
Type, compile and execute the above program in the Java Visualizer.
Activity 3:
In
MyForLoopPrint.java,
change the loop conditions so that the numbers 1 through 10
are printed. Also add a print statement after the
for-loop, that prints anything.
Observe:
- We are able to print out the for-loop variable i.
- We see that the loop is executed five times.
⇒
Each time the variable i has increased its value.
Mental execution:
- When execution first reaches the for-loop, the initialization
condition is executed:
- Say to yourself "i is set to 0".
- Important: the initialization condition is not executed
again, unless we somehow return to the statement preceding
the for-loop.
(We'll see later how this can happen.)
- Next, we test the re-entry condition:
- Here, you say to yourself "Is i less than 6?"
- The answer is "yes", so we enter the loop-body and execute
what's inside.
- Execute (just once) what's inside the loop body:
- In this case the value of i, which is now 0,
gets printed out.
- After the loop-body has executed, we apply
the increment
- Say to yourself "Now i has the value 1".
- Next, we test the loop-entry condition:
- Here, you say to yourself "Is i less than 6?"
- It clearly is, so we enter the loop again.
- Once again, we execute the loop body:
- Because i has the value 1, this is
what gets printed.
- Once again, we apply the increment:
- Say to yourself "Now i has the value 2".
- Next, apply the entry condition ... and so on.
- Finally, after the 5-th time through, i will
get the value 6.
- The re-entry condition now fails, and we exit the for-loop.
- What "exit" means:
- Execution continues to just after the for-loop.
- In this program, we didn't have anything else, but
we could have had:
- In this case, you would print "MOO!" to the screen.
- Another way to understand how it works:
The for-loop is one of several such strange and
fascinating creatures (structures) we'll encounter in
programming. There are many ways to set up a for loop, which we'll explore more
in the homework problems and exercises, especially as we combine looping with
arrays.
1D arrays: A group-of-items variable
First, recall our "box" analogy for a variable:
- Suppose we declare and assign a value to an int
variable i
- The variable is like a "box" that can hold one
single integer value at a time.
An array allows you to create a connected sequences of such boxes:
Let's trace through that example together in the Java Visualizer
to understand how arrays are created and updated in memory.
Some items to keep in mind from above:
- You must specify the size of your array upfront. You can either do this by declaring the array and its
contents between curly braces (line 7), or specifying the size and filling the array in later (line 10).
- An array can only contain one type of item, which the compiler enforces. We used integers here, but we could
have created an array of doubles, or even an array of arrays or more complicated objects (more later!).
- Java will set an empty array (with a known size) to be full of default values. Here, for integers, that
default value was 0; for other types, they will have different default values (0.0 for double, false for
boolean, and null for strings and other objects).
- Array indicies start at 0 and you cannot index past the end of an array.
- You can get the length of an array using the length field of the array instance.
Activity 4:
Modify the code above in the Visualizer to set array2 = array1. What happens?
Arrays and for-loops
Arrays go hand-in-hand with for-loops.
Let's trace through this code together in the Java Visualizer.
Activity 5:
Modify the code above in the Visualizer to have it print out the array elements in reverse.
Activity 6:
Modify the code above in the Visualizer to have it print out every other element.
You should remember the following template for using for loops with arrays:
- Choosing what to set i to controls if you start at the beginning or end of the list;
- Similarly, you'll want to update the conditional in the middle to define when to stop. Is it at the
end of the array, the beginning, or somewhere else?
- Finally, you can choose to increment, decrement, or do something else in the update clause at the end.
- The code in the for loop is just Java code -- you can rename i to be whatever you want (and
sometimes it even makes sense to give it a more useful variable name for clarity). Similarly, the code inside
the for loop can be any valid Java code.
Assignment and copying with arrays
When assigning any two variables of matching type with =, remember that we always just
"copy the box." This is also true for arrays, as long as we understand that in terms of what an
array "stores in the box," this is not a list of values (even though it looks like it), but is
actually a memory address of the start of the entire array.
Consider the following program:
Let's trace this through in the Java Visualizer first, and then dive deeper on paper to understand
what is going on with memory and how arrays are stored.
Activity 7:
What happens when you try to print out your arrays with System.out.println(array1)? Try it out
in the Visualizer. Now, print out what System.out.println(array1 == array2) returns. What do you
think is going on?
Explaining the strangeness of arrays: a peek under the hood
Let's focus on three aspects of arrays that seem to beg
explanation:
- Why do array indices start from 0?
⇒
That is, why start with A[0]
and not
A[1]?
- How come when one array variable is assigned to another,
both seem to affect the same contents?
- What are those strange things printed out when we have
// Something strange:
System.out.println (array1);
To understand why, we're going dig a little deeper:
- Remember the "boxes" idea?
- Variables like
int i = 5;
have a box in memory. Here, the value
5
gets into the box.
- We'll now understand these "boxes" in a little more detail.
- Think of a computer's memory as a long list of boxes,
where each box has a number:
- A box's number is like the number of a apartment mailbox.
- The numbering starts from 0 and goes up to the size of
the memory (which can be quite large).
- Every variable lives somewhere in memory.
- Let's use this example to further explain:
// A simple 4-element array of integers:
int[] A = {25, 36, 49, 64};
// Declare B, assign it A
int[] B = A;
// Use a for-loop to print B
for (int i=0; i<4; i++) {
System.out.println (B[i]);
}
// Modify the 4-th element of B:
B[3] = 81;
// Print the 4-th element of A:
System.out.println (A[3]); // Prints 81
- First, let's examine this conceptual picture of memory:
- Examine the box for
i:
- It so happens that this particular time
(the Java system determines this) that
the box for
i:
is at location 8673.
- This location can change with every new execution of the
program but stays fixed during an execution.
- When the for-loop executes, this is where the values
0,1,2,3
will live, as the iterations proceed.
- Now, the box for
A
is a little different:
- Inside the box is an address of another box: 1032.
- If we look at box #1032, we see that that is where the
first element of the array
25
happens to reside.
- This is no coincidence. It is by design.
- Next, notice that all the array elements of
A
are contiguous, one after another.
This too is by design.
- To access array elements, what really happens under the
hood is this:
- The first element,
A[0],
is at 1032 + 0.
- The second element,
A[1],
is at 1032 + 1.
- The third element,
A[2],
is at 1032 + 2.
- And so on.
- We don't do this in our program, but underneath, that
is what Java is doing when the program runs.
- In fact, in a for-loop, the i-th element,
A[i],
would be found at 1032 + i.
- So, the computer "goes" to location 1032 + i
to fetch its contents as
A[i].
Next, let's consider what happens when
int[] A = {25, 36, 49, 64};
int[] B;
B = A;
System.out.println (B[0]);
gets executed.
- The state of memory is now:
- Here, we say "B points to the same place A is pointing to".
- That place is the start of the actual array contents (1032
in this case).
- Thus, when we execute the println
int[] A = {25, 36, 49, 64};
int[] B;
B = A;
System.out.println (B[0]);
B
has the value 1032 and so
B[0]
refers to the first element, which means 25 gets printed out.
Next, consider what happens next:
int[] A = {25, 36, 49, 64};
int[] B;
B = A;
System.out.println (B[0]);
B[3] = 16;
System.out.println (A[3]);
- The assignment
B[3] = 16
changes the 4-element of the array "pointed to" by
B.
- So, the picture of memory now becomes:
- Thus, the value in
A[3]
is now 16.
Lastly, what was that strange output we saw when we executed
// Something strange:
System.out.println (A);
- What we see is the address in
A
being printed out.
- If the address were actually 1032 (as in our picture)
that is what would get printed out.
- However, Java's address list is quite large and most
addresses have many digits (not four as in 1032).
- In fact, it's so large that the decimal system proves
inconvenient, which is why hexadecimal numbers (base 16)
are used. These have the letters a,b,c,d,e,f in them.
- We don't need to understand how "hexadecimal". Just think
of those as addresses, like a mailbox address.
Keep in mind you can declare an empty array with int[] arr = {};
When things go wrong
Which of the following have errors (syntax or bugs)
and what are they? Can you tell just by reading?
Don't pay attention to the purpose - just whether
the program will compile or run.
Activity 10:
Activity 11:
Activity 12:
Next class:
We'll go through some more in-class exercises on debugging using the terminal and the Java Visualizer.
Assignments for next lecture:
For extra credit, post your buggy code for anything we've worked on in class so far. If it gets selected
during the next class, you'll receive the extra credit!