Module 0: Arrays: A first look


Objectives

 

By the end of this module, for simple programs with real numbers, you will be able to:

Note: As usual, for all modules, when you solve an exercise you will either write a program or write an answer in the appropriate module PDF. Some exercises will feature both a program and an answer to be written in the PDF. Thus, for this module, your non-program answers will be in module0.pdf, whereas for other modules, use the appropriately named pdf.
 

0.0 Audio:
 


0.0    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:

  • The analogy for an array: a "box" that stores a group of related items arranged linearly:

  • The code that created the above:

  • Note: the following (which we could do with plain integers) is not allowed in Java:

  • Here is another way of accomplishing the assignment:

    Note: Java uses square brackets for arrays, in three ways.

    1. A declaration can declare an array variable with empty square brackets (nothing in between).
          int[]  A = new int [5]
          
    2. A number in between the brackets can indicate the size of the array, as in "5" above.
          int[] A = new int [5]
          
    3. A number in between can access a particular element of the array, as in each assignment above.
          A[0] = 25;
          

  • Perhaps the most common way of declaring and using arrays looks like this:

  • Next, let's print the elements of the array:

 

0.1 Exercise: Modify the above program (ArrayExample5.java) to include a sixth element so that the elements in order are: 36, 25, 16, 9, 4, 1. Print the elements and the array length.
 

Let's look at a few details carefully:

  • First, the declaration:

    • It starts with the type of value in each location of the array. Here: int.

    • Then the square brackets together.

    • Finally, the array variable name. Here it's A.
             ⇒ This is a name we chose.

  • Next, unlike int's or double's, we need to explicitly create space for arrays:

    • Uses the reserved word new:

    • Recall: reserved words are words in the language that can't be used as variable names, method names and the like.

    • This is followed by the type (here, int):

    • Which is followed by the desired size in square brackets:

    • Note: the value of the int in n is used for the array size.

  • So, when do we use

    versus

    • Use the latter for small arrays when all the array contents are known ahead of time.

    • Use the former in all other cases.

  • The value used to access one of the array locations is called an array index:

    • The first location always has index 0.
    • If the size is n, the last index is n-1.
    • An array index is always a non-negative integer between 0 and the size of the array minus 1.

  • Every array variable has an associated length:

    • This length is an integer.
    • Note: the length is NOT the last index.
             ⇒ It is one more than the last index.
    • The length will be useful later.

  • In the example above, we (laboriously) wrote a println for each array element.
           ⇒ We'd never do this in practice: instead, we'd use a for-loop (see next section).
 

How to read aloud:

  • When you see

    say to yourself

    • Oh, A is an double array.
    • Its space of n elements has been created with the new operator.
    • The values inside of A aren't yet initialized.
    • B is another array, an int array, fully initialized to 5 values.
    • In the for-loop where i runs from 0 to n, "A's of i" is assigned 1 over "B's of i".

  • When you see
        A[0] = 25;
        
    say to yourself any of the following:
    • "A zero is assigned 25"
    • "A of zero is assigned 25"
    • "A's of zero is assigned 25" or
    • "A0 (sub zero) is assigned 25".
 

Let's now deliberately make a mistake to see what happens: we'll use an index that's "out of bounds"

 

0.2 Exercise: Edit, compile and execute the above program to see what happens.
 


0.1    Arrays and for-loops

 

Arrays go hand-in-hand with for-loops.

  • Note that the loop variable is used as the array index:

  • Here, the loop variable i takes values 0, 1, 2, 3, 4 in successive iterations.

  • For example, in the third iteration
    • i is 2.
    • Therefore, A[2] is given to System.out.println.
    • The value of A[2] is 9 (third element).
    • The value printed is therefore 9.
 

We can use for-loops for filling in the elements of an array, e.g.,

  • Here, we are cleverly using the for-loop variable i for two different purposes:
    • One is to serially traverse the array (the second for-loop).
    • The other purpose is to use its value in an arithmetic expression, whose value is eventually stored in the array (first for-loop).
     

    0.3 Exercise: In module0.pdf, trace through the first for loop iteration by iteration, and draw the entire contents of the array at each step. Note: for this and other tracing exercises, you can save time by writing or drawing on paper and then including a picture of the paper in the PDF (as long as it's clear and legible).
     

    0.4 Video:

     

    0.5 Exercise: In a file called MyArrayWithForLoop.java, modify the above program to include a sixth element, 36.
     

    0.6 Exercise: In a file called MyArrayWithForLoop2.java, change the order of elements so that the order is: 36, 25, 16, 9, 4, 1. That is, the first element in the array (A[0]) is 36, the second is 25 ... etc. Print the elements.
     

    0.7 Exercise: In MyArrayWithForLoop3.java, change the printing for-loop from MyArrayWithForLoop.java, to print the elements in reverse order (starting with A[5]).
     

    0.8 Video:

     


    0.2    Arrays of double's

     

    Consider the following example:

    • Here, we have declared an array variable A for an array of double values:

    • And created space for it (5 slots):

    • Then, we assigned values in a for-loop:

    • And used those values in printing:

     

    0.9 Exercise: In MyDoubleExample.java, modify the above code to compute the sum (of the first n terms) of the series s = 1 + 1/22 + 1/32 + ... + 1/n2. Then, compute the value 6s (6 times s) and find its square root using Math.sqrt() (remember?) Try this for different and possibly values of n, finally submitting your program with n set to 25. What do you think the square root of 6s turns out to be for large n? Recall how we computed fundamental math constants in Unit 0.
     


    0.3    Default values

     

    Consider this program:

     

    0.10 Exercise: What does the above program print? Write it up in DefaultExample.java, What do you conclude about the default values placed inside an array? Is it the same for integer arrays?
     

    0.11 Audio:
     


    0.4    Assignment and copying with arrays

     

    Consider the following program:

    • Here, we have created a second array B:

    • Each element is copied one by one:

     

    0.12 Exercise: In module0.pdf, draw out each array and trace through both for-loops showing, step-by-step in pictures, how elements get copied from one array to the other.
     

    0.13 Video:

     

    Now for something strange:

     

    0.14 Exercise: What does the program print? Write it up in CopyExample2.java and see. Then, draw the box pictures for both arrays and see if that explains the result.
     

    About array copying and assignment:

    • Yes, it's a little strange.

    • When array variables are used in direct assignment, as in

      • The variable B gets associated with the same array space as A's.
      • We say that "A and B point to the same array space".

    • This is why B[0] and A[0] are the same "box".

    • This is true for the other slots.

    • For example, B[1] and A[1] are the same "box" too.
     

    0.15 Exercise: Does it work the other way around?

    Write this up in MyCopyExample2.java. What gets printed out?
     

    0.16 Exercise: Consider this program:

    
    public class CopyExample3 {
    
        public static void main (String[] argv)
        {
    	int n = 5;
    
    	double[] A = new double [n];
    	for (int i=0; i<n; i++) {
    	    A[i] = 1.0 / ((i+1)*(i+1));
    	}
    
    	// What is the value of A[0] here?
    	System.out.println (A[0]);
    
    	// Something strange:
    	System.out.println (A);
    
    	double[] B = new double [n];
    
    	A = B;
    	// What is it here?
    	System.out.println (A[0]);
    
    	// Weirdness once again:
    	System.out.println (A);
    	System.out.println (B);
        }
    
    }
        
    Write this up in CopyExample3.java. What gets printed out? Explain the different values of A[0] (in module0.pdf).
     

    0.17 Video:

     


    0.5    Explaining the strangeness of arrays: a peek under the hood

     

    Let's focus on three aspects of arrays that seem to beg explanation:

    1. Why do array indices start from 0?
             ⇒ That is, why start with A[0] and not A[1]?

    2. How come when one array variable is assigned to another, both seem to affect the same contents?
      	// An array A:
              int[] A = {25, 36, 49, 64};
      	// An array B:
              int[] B;
      	// What appears to be a copy:
              B = A;
      	// Prints 25 as expected:
              System.out.println (B[0]);
      
              B[3] = 16;
              System.out.println (A[3]);
      	// Prints 16 (unexpectedly), implying that A[3] has been changed to 16
          
      Why exactly, when we set the first entry of B to 0 did the first entry of A also get set to 0?

    3. What are those strange things printed out when we have
      	// Something strange:
      	System.out.println (A);
          
     

    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.
     


    0.6    More examples

     

    Example 1: let's write a program to "rotate" an array:

    • Thus, we want to shift each element into the next position rightwards.

    • The last element gets into the first position.

    • First, we'll use the help of an additional array to make this happen:

      • We first copied elements A[0],...,A[n-2] into locations ("boxes") B[1], ..., B[n-1].
      • Then, we copied A[n-1] into B[0].
      • The array B is in the desired form.
      • Lastly, we copied all of B into A so that A has the desired rotation.
       

      0.18 Video:

       

      0.19 Exercise: In module0.pdf, draw out each array in each step, showing the contents.

    • It is possible to do without an additional array:

     

    0.20 Exercise: In module0.pdf, trace through the above example (RotateExample2.java) by hand to see how it works. Then, also trace through (in same PDF) the version below and explain why it does not work:

     

    Next, in the second example, we'll write code to reverse an array:

    Here's a version that uses an additional array:

     

    0.21 Exercise: Trace through the program above (in module0.pdf).
     

    0.22 Exercise: Write code in MyReverseExample.java to achieve reversal in the same array A, without using an extra array like B.
     

    0.23 Audio:
     

    Example 3: adding two arrays

    • Here, we have added two arrays element-by-element.

    • The resulting values are put into a third array (C).
     

    0.24 Exercise: Trace through the program above (in module0.pdf).
     

    0.25 Exercise: Write a different version (in AdditionExample2.java) that takes an array A and computes the following sum of two arrays, element by element, as in the above example. However, in this case, you are to compute the sum (element-by-element) of the array A and its reversal, putting the result into array B. Print the contents of array B, and to test your program use the same contents for array A as above. Do this without using a third array.
     

    0.26 Exercise: In module0.pdf, trace through the program below by hand.

    What does it print?
     

    0.27 Exercise: In module0.pdf, trace through the program below by hand.

    What does it print?
     

    0.28 Exercise: In module0.pdf, trace through the program below by hand.

    What does it print? Look up the definition of factorial somewhere and relate that to the computation above.
     

    0.29 Video:

     

    Note: in the exercise above, we learned a few new things about using arrays:

    • Sometimes, we create one additional slot so that we can use the index A[n] when convenient.

    • A for-loop can have other variables that change with the loop besides the loop variable.
     


    0.7    Reading and writing

     

     

    Let's look at an example and how to read the code:

    • First, look for the array declaration and identify the type (e.g, int or double etc):

      • Say to yourself, "A is an array that holds double's".
      • "B is an array that holds int's".

    • Then, look to see how much space is created:

      • Say to yourself, "A's size is 5, created using the new operator".
      • "A's elements are all initially 0".
      • "B's size is 5, directly initialized to particular values".

    • Now, check that, when variables are used to access array components (slots), the bounds are right:

     

    Writing:

    • Writing the declaration:

      • No spaces between either the type (double), nor between the brackets.
      • A single space between the right bracket the array variable name.

    • Creating space:

      • A space on either side of the assignment operator, =.
      • A space on either side of the new reserved word.
      • The space after the double is optional. (Here, we have a space.)
      • No spaces for the size and brackets.

    • Prefer the use of the new operator except for small-ish arrays whose initial contents are known and fixed.

    • No spaces when using the values in an array:

     

    Using the array's built-in length. Consider this variation:

    • We could either n or the array's length A.length.

    • So, which one should we use?
      • Generally, when the limit is a mathematically meaningful variable like n above, you can use n.
      • Otherwise, use the array's length.

    • Sometimes, using an array's length variable can make a program needlessly verbose.
      • For example, here's the array-reversal example using the length variable:

     

    Variable names:

    • Here is the first length example, re-written with different variable names:

    • For a program this short, it is probably a bit verbose.

    • Notice: we used different names for the loop variables in the two for-loops.
     


    0.8    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.
     

    0.30 Exercise:

     

    0.31 Exercise:

     

    0.32 Exercise:

     

    0.33 Exercise: The following program has multiple errors. See if you can spot them by reading. Then in ErrorExample4.java fix the errors by editing the program. The array A needs to be an array of double's.

     

    0.34 Audio:


    On to Module 1



    © 2017, Rahul Simha