Lecture 19: Arrays, Variables, and For-loops
Overview
Using command-line arguments via arrays
Using loops and variables to iterate over array contents
10.1 Arrays
We saw, in lecture, how we can use linked lists to represent lists of arbitrary length. This structure is useful in many contexts, and we have some practice with writing recursive methods on linked lists to perform differen operations.
One thing linked lists are less convenient for is index-based access. That is, if we want the 1st or 2nd or 3rd or nth element in a linked list, it’s somewhat clunky and inefficient to access it. When this kind of random access is required—where we want to access elements by index and not process them all at once—different structures are more useful. One of these that Java (along with many, many, other languages) provides is called the Array, which holds a fixed-size, indexable collection of values.
In this section, we’ll see some uses of Arrays that are built-in to Java, how to create them on our own, and how to process their contents with loops.
10.1.1 Arrays and main
One of the most common places we confront Arrays in Java is in the main method. Recall from lecture the structure of the main method:
class SampleMain { public static void main(String[] args) { // The java command starts running here, with any values provided } }
When we run the java command, the Java system collects the values provided on the command line, puts them all into an Array of Strings, and passes a reference to that Array as the argument to the the main method (args in the above example). So, for example, if we compile the above class and run it:
$ java SampleMain 10 20 30 |
The parameter args will contain a reference to an array containing the strings "10", "20", and "30". Pictorially, we’d draw this as:
Arrays, just like objects, take up space on the heap (not on the stack), and parameters, variables, and fields store references to them. The components of an array are stored at indices counting up from 0.
We can access the elements of arrays by using the array index expression on a reference to an array. So, for example, we could print out the second element of the array above (the value at index 1) by writing:
class SampleMain { public static void main(String[] args) { System.out.println(args[1]); // prints 20 } }
The expression args[1] first looks up the reference stored in args, and then the value in the index labelled 1. We can put any expression in the brackets that computes an int. We could write args[1 + 1] and get the value at index 2 ("30" in this case), for example.
Do Now!
What happens if you provide an index that isn’t in the range of 0-2 for this example?
10.1.2 Variables, and Accessing Elements with a Loop
So far we’ve just seen how to access elements with a number we knew ahead of time when writing the program. In lecture, we saw how to use an element-wise loop, also known as a for-each loop, along with a variable, to get access to each of the elements of the array in turn, and aggregate their values together:
class SumMain { public static void main(String[] args) { int total = 0; for(String s: args) { total = total + Integer.parseInt(s); } System.out.println(total); // prints 70 if run with // $ java SumMain 40 20 10 } }
There are a a few ideas happening here:
The variable total is created with the starting value 0, and then gets updated by the variable assignment in the body of the loop.
The loop started with for runs the loop body—the expressions in between the curly braces—once for each element in the Array referenced by args.
Each time the loop body is run, the variable s gets its current value set to the next one of the elements, starting with index 0.
As the program runs, we could visualize the changes like this:
We can also think about these values in tabular form. We often use the word iteration to mean “one run of the loop body”:
Iteration |
| Value of s |
| total before |
| total after |
1st |
| "40" |
| 0 |
| 40 |
2nd |
| "20" |
| 40 |
| 60 |
3rd |
| "10" |
| 60 |
| 70 |
These element-wise or for-each loops are extremely useful when the program needs to process all the elements of an array. The general form is:
for(Type t: arr) { ... loop body ... }
This is actually a slight simplification; for-each loops work for types other than arrays. We’ll see that in detail later on.
Where Type is the type of elements (like int, String, or Tweet) and arr is an array containing elements of that type. The variable t is up to us, the programmer, as with parameters and field names. The loop body is any sequence of statements, and these statements can use the declared variable.
Exercise
Write a program that uses a for-each loop to take in all the command-line arguments, concatenate them together with a semicolon after each one, and print out the result. For example:
$ java ConcatMain one two three
one;two;three;
Exercise
Write a program that uses a for-each loop to take in a sequence of numbers and print out their mean.
10.1.3 More Control with Counted for Loops
If we want to write a program that uses a subset of the values in an array, we need a loop that provides more control than simply visiting every element. A common way to manage these situations is with a more general version of the for loop.
Consider a calculator-like program that takes as the first command-line argument an operation to perform, followed by a list of numbers:
$ java CalcMain sum 3 4 5 |
12 |
$ java CalcMain product 3 4 5 |
60 |
$ java CalcMain mean 1 2 3 5 5 8 |
4 |
To implement this, we need two pieces:
First, to choose the operation to perform by looking at the first element of the array
Second, to perform the aggregation on the remaining elements
The first part is simple to accomplish by looking at the element at index 0. The second part requires looking at all the remaining elements, starting at index 1. Here’s how we would write a program to tackle this:
class CalcMain { public static void main(String[] args) { if(args[0].equals("sum")) { int total = 0; for(int i = 1; i < args.length; i = i + 1) { total = total + Integer.parseInt(args[i]); } } else if(args[0].equals("product")) { int prod = 1; for(int i = 1; i < args.length; i = i + 1) { prod = prod * Integer.parseInt(args[i]); } } else if(args[0].equals("mean")) { // exercise below } } }
The key part of this is the new loop structure, which has the same beginning in both the sum and product cases:
for(int i = 1; i < args.length; i = i + 1) { ... loop body ... }
The way such a loop runs is as follows:
Run the initialization statement – the one before the first semicolon.
Evaluate the condition – the expression before the second semicolon. If it evaluates to false, stop running the loop. Note that this means the loop could run zero times, if the condition is false at the start.
Evaluate the loop body
Evaluate the update expression – the last of the three parts of the loop header.
Go back to the step in this list checking the condition expression, and proceed from there.
The changes for this example would look like:
Again, in tabular form we could write it as:
Iteration |
| Value of i |
| Value of args[i] |
| total before |
| total after |
1st |
| 1 |
| "3" |
| 0 |
| 3 |
2nd |
| 2 |
| "4" |
| 3 |
| 7 |
3rd |
| 3 |
| "5" |
| 7 |
| 12 |
Note that the loop doesn’t run for a fourth iteration, because that’s when the condition first evaluates to false. This is because the value i gets updated to 4, which is not less than the value args.length.
Exercise
Fill in the tabular form for the product example above:
Iteration
Value of i
Value of args[i]
prod before
prod after
Exercise
Fill in the "mean" case above.
10.2 Summary
Arrays are fixed-size, indexable collections of elements. So far, we’ve just seen arrays of Strings, which is how Java passes command-line arguments.
Variables are names whose values can change over time via variable assignment.
For-each loops or element-wise for loops can be used to run expressions for each element in an array.
Counted for loops can be used to visit a subset of elements by controlling the specific indices to visit.