Start Lecture #9
Homework: 5.17, 5.35.
One big advantage of methods is that users of a method do not need to understand how the method is implemented. The user needs to know only the method signature (parameters, return value, thrown exceptions) and the effect of the method.
Although I remember enough calculus to derive the Taylor series for sine, that might not be the best way to implement sin(). Moreover, I would need to work very hard to produce an acceptable implementation of atan(). Nonetheless, I would have no problem writing a program accepting a pair (x,y) representing a point in the plane and producing the corresponding (r,θ) representation. To do this I need to just use atan, not implement it.
As mentioned previously, when given a problem that by itself is too complicated to figure out directly, it is often helpful to break it into pieces. A very simple example was in the previous section. Separating out pickChar() made the main program easy and pickChar itself was not very hard.
We haven't done anything large enough to illustrate top-down design in all its glory. The idea is to keep breaking up the problem. That is, you first specify a few methods that would enable you to write the main() method.
Then for each of these new methods M you define other
submethods
that are used to implement M and the
process repeats.
The figure on the right (a very slightly modified version of figure 5.12 from the 8e) shows this design philosophy applied to the problem of printing a calendar for any given month.
You can guess the functionality of most of the methods from their name (an indication of good naming). Only getTotalNumberOfDays requires explanation. It calculates the number of days from 1 January 1800 to the first day of the month read from the input.
Given a fleshed out design as in the diagram, we now need to implement all the boxes. There are two styles: top-down and bottom-up.
In the first, you perform a pre-order traversal of the design. That is, you start by implementing the root calling the (unimplemented) child methods as needed.
In order to test what you have done before you are finished, you
implement stub
versions of the methods that have been called
but not yet implemented.
For example, the stub for a void method such
as printMonthBody might do nothing or might just print
something like printMonthBody called.
Stubs for non-void methods need to return a value of the correct type, but that value need not be correct.
In a bottom-up approach, you perform a post-order traversal of the design tree. That is, you only implement boxes all of whose children have been implemented (we are ignoring recursive procedures here). In the beginning this means you implement a leaf of the tree.
Since you implement the main program last, you need to write test programs along the way to test what you have implemented so far.
Read. In particular read over the full implementation of printCalendar.java
Arrays are an extremely important and popular data structure that are found in essentially all programming languages. An array contains multiple elements all of the same type.
In this chapter we restrict ourselves to a 1-dimensional (1D) array, which is essentially a list of values of a given type. The next chapter presents the more general multi-dimensional array.
In Java the ith element of a 1D array named myArray is referred to as myArray[i]. Note the square brackets [].
main(String[] args)
In the example on the right, which we have used in every program we have written, the only parameter args is a 1D array of Strings.
Note the syntax, first the base type (the type of each element of the array), then [], and finally the name of the array.
double[] y;
When the array is not a parameter as above, but is instead a locally declared variable, the syntax is essentially identical and is shown on the right. The only difference being a semicolon added at the end.
You may recall that early in the course I sometimes wrote String args[] and noticed that it worked as well as String[] args. In fact this has been added to Java just so that C language programmers would be comfortable. However, the String[] args format is preferred.
There is an important difference between declaring scalars as we have done many times previously and declaring an array. When the scalar is declared, it can be used (i.e., given a value). For an array another step is needed.
double[] x; x = new double[10];
double[] x = new double[10];
The declaration double[] x, declares x but does not reserve space for any of the elements x[i]. This must either be done separately as shown on the above right or written together as shown below the line.
The new operator allocates space, in the example on the right, enough space for 10 doubles. The effect is that the array x can hold 10 elements.
In java the first element always has index 0, so x consists of x[0], x[1], ..., x[9].
The size of an array is fixed; it cannot be changed.
To retrieve the size of a 1D array is easy, just write name.length. For example x.length would be 10 for the example above.
Another difference between arrays and scalars is that when the array is created (using the new operator), initial values are given to each element.
for (int i=0; i<x.length; i++) { x[i] = 34; }
As suggested above, elements of a 1D array are accessed by giving the array name, then [, then a value serving as index, and finally a ]. For example, the code on the right, sets every element of the array x to 34.
Note the use of .length.
Creating an integer array containing the values 1, 8, 29, and -2 is logically a three step procedure.
int[] A; A = new int[4];
int[] A = new int[4];
int[] A; A = new int[4]; A[0]=1; A[1]=8; A[2]=29; A[3]=-2;
int[] A = {1,8,29,-2};
We have already seen how the first two steps can be combined into one java statement. For example see the first two sections of code on the right.
In fact the Java programmer can write one statement that combines all three steps. The third code group on the right can be written much more succinctly as shown on the fourth group.
Note that the usage of curly brackets as shown is restricted to initialization and a few other situations, you cannot use {1,8,29,-2} in other places.
public class Test { public static void main(String[] args) { int[] a = new int[10]; int[] b = new int[10]; int[] c = new int[10]; for (int i=0; i<a.length; i++) { b[i] = 2*i; c[i] = i+100; a[i] = b[i] + c[i]; // 3i+100 System.out.printf("a[%d]=%d b[%d]=%2d c[%d]=%d\n", i, a[i], i, b[i], i, c[i]); } } }
Very often arrays are used with loops, one iteration of the loop corresponding to one entry in the array.
In particular for loops are often used since they are the most convenient for stepping through a range of values. The first entry of the array is always index 0 and the last entry of an array A is A.length-1.
For example the simple code on the right steps through three arrays, setting the value of two of them and calculating from these values the value of the third.
We have seen several short-cuts provided by Java for common
situations.
Here is another.
If you want to loop through an array accessing (but
not changing) the ith element
during the ith iteration, you can use the
for-each
variant of a for loop.
for(int i=0; i<a.length; i++) { | for(double x: a) { use (NOT modify) a[i] | use (NOT modify) x } | }
Assume that x has been defined as an array of doubles.
Then instead of the standard for loop on the near right,
we can use the shortened, so-called for-each
variant on the
far right.
import java.util.Scanner; public class StdDev { public static void main(String[] args) { Scanner getInput = new Scanner(System.in); System.out.println("How many numbers do you have?"); final int n = getInput.nextInt(); System.out.printf("Enter the %d numbers\n", n); double[] x = new double[n]; double sum=0; for (int i=0; i<n; i++) { x[i] = getInput.nextDouble(); sum += x[i]; } double mean = sum / n; double diffSquared=0; // std dev is sqrt(diffSquared/n) for (double z : x) { diffSquared += Math.pow(mean-z,2); } System.out.printf("Mean is %f; standard deviation is %f\n", mean, Math.sqrt(diffSquared/n)); } }
Read
Given n values, x0, ..., xn-1, the mean (often called average and normally written as μ) is the sum of the values divided by n.
The variance is the average of the squared deviations from the mean.
That is, [ (x0-μ)2 + ... +
(xn-1-μ)2 ] / n
A Java program to calculate these two values is on the right.
Note that the first loop, which modifies the x array, must
use the conventional for loop; whereas, the second loop,
which only uses the x array can use the for-each
variant.
public class DeckOfCards { public static void main(String[] args) { int[] deck = new int[52]; String[] suits = {"Spades", "Hearts", "Diamonds", "Clubs"}; String[] ranks = {"Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"}; // Initialize cards for (int i = 0; i < deck.length; i++) deck[i] = i; // Shuffle the cards for (int i = 0; i < deck.length; i++) { // Generate an index randomly int index = (int)(Math.random() * deck.length); int temp = deck[i]; deck[i] = deck[index]; deck[index] = temp; } // Display the shuffled deck for (int i = 0; i < 4; i++) { String suit = suits[deck[i] / 13]; String rank = ranks[deck[i] % 13]; System.out.println("Card number " + deck[i] + ": " + rank + " of " + suit); } } }
Let's look at this example from the book with some care. I downloaded it from the companion web site. You can download all the code examples.
The idea is to have a deck of cards represented by an array of 52 integers, the ith entry of the array represents the ith card in the deck.
How does an integer represent a card?
First of all the integers are themselves between 0 and 51.
Given an integer C (for card), we divide C by 13 and look at the
quotient and remainder.
The quotient (from 0..4) represents the suit of the card and the
remainder (from 0..12) represents the rank.
Look at the String arrays; they give us a way to print the name of the card given its integer value (by using the quotient and remainder).
The deck initialized to be in order
starting wit the Ace of
Spades and proceeding down to the 2 of Clubs (Liang had Clubs and
Diamonds reversed).
Notice how the deck is shuffled: The card in deck[i] is swapped with one from a random location. This can move a single card multiple times. Notice the 3 statement sequence used to swap two values.
Liang printed the first 4 cards; I print the entire deck.
The last loop could be written as a one-liner
.
for (int C : deck) System.out.printf("Card number %2d: %s of %s\n", C, ranks[C%13], suits[C/13]);
Serious business. We can no longer put off learning about references.
Consider the situation on the near right. We have created two arrays each holding 6 integers. The first has been initialized, the second has not. The goal is to make the second a copy of the first.
Note that the array name is not the array itself. Instead it points to (technically, refers to) the contents of the array. One consequence is that the size of Array1 does not depend on the number of elements in the array, which turns out to be quite helpful later on.
If we simply execute Array1 = Array2;, we get the situation in the upper right. Both arrays refer to the same content. In this state, changing the contents of one, changes the other.
If the goal is to get the situation in the lower right, you need a loop to copy each entry (or use some built in method that itself has a loop).
int a, b; a = 5; b = a; b = 10;
Look at the simple code on the right. According to the picture with arrays, after we execute b=a;, both a and b will refer to the same value, namely 5. Then when we execute b=10;, both a and b will refer to the same value, namely 10.
But this does NOT happen. Instead, b==10, but a==5 as we expected.
Why?
The technical answer is that primitive types, for example int, have value semantics; whereas, arrays (and objects in general) have reference semantics.
Looking at the diagram on the right we see that the array Array1 refers to (or points at) the actual array where the values reside; whereas, the int a and the int b ARE the values 5 and 10. There are no pointers or references involved.
When you change a or b, you change the value; when you change Array1, you change the reference (pointer).
Liang first talks about passing the entire array and then about passing individual members. I will reverse the order since I believe passing individual members is easier.
There is very little to say about passing an element of a one dimensional array as an argument. The element is simply a value of the base type of the array.
public static void printSum(int x, int y) { System.out.println(x+y); }
On the right is the simple printSum method that we saw earlier. It has two integer parameters. If our main method has a declaration int[] A=new int[10], then we can write, printSum(A[4],A[1]). The printSum() method will add the two values and print the sum just as if it was invoked via printSum(8,2);
public static void printArray(int[] A) { for (i : A) { System.out.println(i); } }
The printArray() method on the right has an (entire) int array as parameter. To call the array is easy: printArray(B) where B is a single-dimensional array of integers. Note that printArray can handle any size array.
The tricky part is the melding of pass by value method invocation and reference semantics for arrays, which we address next.
Assume we have a method, such as printArray that has an array parameter A and that we call it with an array argument named B.
At the point of the method call the value in B is copied to A, but, as we know, when the method returns any new value in A is NOT copied back to B. This rule still holds for arrays; no change.
The diagram at the right show the configuration when the method is called. Even, if the method changes the value in A, the value in B will not change. It will remain a pointer to the 6 element structure shown.
But now consider what happens if the called method executes A[0]+=1;. This changes not the value in B (a pointer) but instead changes the value in a cell pointed to by B. Hence when the method returns, assuming nothing else happened, the value in B[0] has been increased.
Summary, the call above cannot change the value B, but it can change the value of an individual B[i].
Java also has anonymous arrays, i.e., arrays without a name. So you can write printArray(new int[]{1,5,9});