Arrays 2

Initialized arrays of Objects

Although the textbook doesn't show you about creating an initialized array of objects, it should. The initialization of an object at an array position is just:
new MyClass(...)
All we have to do is put them into the array intialization syntax
{ ... }

BankAccounts Example

If I knew the starting balances without need of user entry I could do this:
BankAccount[] accounts = {
  new BankAccount(500),
  new BankAccount(300),
  new BankAccount(700),
}
As proof of concept, we again refer to the textbook BankAccount class:
BankAccount.java  
This time we will write a simple demo program to illustrate the idea:

ArrayInitAccounts.java
public class ArrayInitAccounts {
 
  public static void main(String[] args) {
    BankAccount[] accounts = { 
      new BankAccount(500),
      new BankAccount(300),
      new BankAccount(700),
    };
 
    for (BankAccount acct: accounts) {
      System.out.println(acct.getBalance());
    }
    System.out.println("-----------------");
 
    for (int i = 0; i < accounts.length; ++i) {
      accounts[i].deposit(50);
    }
 
    for (BankAccount acct: accounts) {
      System.out.println(acct.getBalance());
    }
  }
 
}
select
Observe the enhanced for loop syntax:
for (BankAccount acct: accounts) { ... }
This is precisely the pattern required, it is just that we have not used it so far. Again, if you want to exhibit the index for some reason, the enhanced loop syntax is not useful.

MonthDays revisited

We want to revisit this program:
MonthDays.java  
As I mentioned this uses parallel arrays to match month names to month days. A better approach is to group the month name with the month days into a single class. Minimally the class can be this:
public class MonthNumDays {
  private String month;
  private int days;
 
  public MonthNumDays(String _month, int _days) {
    month = _month;
    days = _days;
  }
  public String getMonth() {
    return month;
  }
  public int getDays() {
    return days;
  }
}
It follows the UML diagram:

MonthNumDays

- String month
- int days

+ MonthNumDays(String, int)

+ String getMonth()
+ int getDays()

Using this class we can write an improved version of the MonthDays program using an array of initialized objects:

MonthDaysImproved.java
public class MonthDaysImproved {
 
  public static void main(String[] args) {
 
    MonthNumDays[] entries = {
      new MonthNumDays("January", 31),
      new MonthNumDays("February", 28),
      new MonthNumDays("March", 31),
      new MonthNumDays("April", 30),
      new MonthNumDays("May", 31),
      new MonthNumDays("June", 30),
      new MonthNumDays("July", 31),
      new MonthNumDays("August", 31),
      new MonthNumDays("September", 30),
      new MonthNumDays("October", 31),
      new MonthNumDays("November", 30),
      new MonthNumDays("December", 31),
    };
 
    // print using regular for loop and use index
    for (int index = 0; index < entries.length; index++) {
      System.out.printf("month #%s, %s, has %s days\n", 
          index+1, entries[index].getMonth(), entries[index].getDays()
      );
    }
 
    System.out.println("----------------------------");
 
    // print using enhanced for loop
    for (MonthNumDays entry : entries) {
      System.out.printf("%s has %s days\n", 
        entry.getMonth(), entry.getDays()
      );
    }
  }
}
select
The "improvement" is that you have grouped the month name with its number of days. Now if there is an omission, or a misordering, it will not effect the correct correspondence of the name to the number of days.

Sequential Search

This is section 7.8 in the textbook. Sequential search is a common algorithm used in many circumstances to find the occurrence of a value within an array. Another name for it is Linear Search. We will write this code as a member function. Here is the textbook version
SearchArray.java  
The idea is pretty simple. Here is an array:
int[] tests = { 87, 75, 98, 100, 82, 88, 100 };
I want to search of an occurrence of 100 and report back. If there are duplicates, I want to go until the first occurrence is found and stop.

One of the important features of this search algorithm is that it reports not only that it is found, but the position where it is found. So what happens if it is not found? It must report something, so what it returns is -1, a non-existent position in the array.

Although the textbook solution is correct, it is unnecessarily difficult. It has to manage two position markers:
index: iterator
element: where the value is found
A simpler solution (in my opinion) is this:
  public static int linearSearch(int[] array, int value) {
    for (int i = 0; i < array.length; ++i) {
      if (array[i] == value) {
        return i;
      }
    }
    return -1;
  }
It breaks the rule of avoiding control transfer out of a loop, but it is quite understandable: If you want to avoid transferring control out of a loop, here is another version:
  public static int linearSearch(int[] array, int value) {
    int index = 0;
    boolean stop = false;
    boolean found = false;
    while (! stop) {
      if (index == array.length) {
        stop = true;
      }
      else if (array[index] == value) {
        found = true;
        stop = true;
      }
      else {
        ++index;
      }
    }
    if (found) {
      return index;
    }
    else {
      return -1;
    }
  }
Here is my version of a test program:

LinearSearch.java
import java.util.Scanner;
 
public class LinearSearch {
 
  public static void main(String[] args) {
    int[] tests = { 87, 75, 98, 100, 82, 88, 100 };
 
    Scanner keyboard = new Scanner(System.in);
 
    System.out.print("search for score: ");
    int score = keyboard.nextInt();
 
    int result = linearSearch(tests, score);
 
    if (result == -1) {
      System.out.println("no test has that score");
    }
    else {
      System.out.printf(
              "first occurrence of %s is for test %s\n", score, result+1);
    }
  }
 
  public static int linearSearch(int[] array, int value) {
    for (int i = 0; i < array.length; ++i) {
      if (array[i] == value) {
        return i;
      }
    }
    return -1;
  }
 
//  public static int linearSearch(int[] array, int value) {
//    int index = 0;
//    boolean stop = false;
//    boolean found = false;
//    while (! stop) {
//      if (index == array.length) {
//        stop = true;
//      }
//      else if (array[index] == value) {
//        found = true;
//        stop = true;
//      }
//      else {
//        ++index;
//      }
//    }
//    if (found) {
//      return index;
//    }
//    else {
//      return -1;
//    }
//  }
 
}
select

Two-Dimensional Arrays

This is section 7.9 in the textbook. A 2-dimensional array is an array of arrays. It is created uninitialized like this:
int[][] scores = new int[3][4];
It is usually depicted as a "matrix" consisting of rows and columns:
scores[0] scores[0][0]scores[0][1]scores[0][2]scores[0][3]
scores[1] scores[1][0]scores[1][1]scores[1][2]scores[1][3]
scores[2] scores[2][0]scores[2][1]scores[2][2]scores[2][3]
We can access individual elements using a pair of indices like:
int value = scores[0][3];  // read value at row 0, column 3
scores[2][1] = 77;         // set value at row 2, column 1
Used with one index only, we get a single row:
scores[row_index]
This is a regular array through which we can iterate by a regular or enhanced for loop. Suppose we want to interate through the row scores[1]
for (int j = 0; j < 4; ++j) {
  // do something with scores[1][j]
}
or
for (int score: scores[1]) {
  // do something with score
}
Iterating through the outer loop of rows can also be done by regular or enhanced for loops:
for (int i = 0; i < 3; ++i) {
  // do something with the row scores[i]
  ...
}
or
for (int[] score_row: scores) {
  // do something with score_row
}
Observe that the type of the row is int[], which is what we would expect by removing one of the bracket pairs.

Here is a demo program which you should key in and complete:

Scores2D
import java.util.Random;
 
public class Scores2D {
 
  public static void main(String[] args) {    
    Random rand = new Random();
 
    int[][] scores = new int[3][4];
 
    for (int row = 0; row < 3; ++row) {
      for (int col = 0; col < 4; ++col) {
        scores[row][col] = 1 + rand.nextInt(100);
      }
    }
 
    /* we want the output to look something like this (aligned right)
         72  11  50  76
         40   1  73   5
          2  22  14  30
     */
 
    // print using a regular for loop in the style in which it was created 
    System.out.println("-----------------------");
 
    // print using a regular for loop without mention of ROWS and COLS
    System.out.println("-----------------------");
 
    // print using enhanced for loops, both outer and inner
    System.out.println("-----------------------");
  }
 
}
The initial creation loop could also be this:
    for (int row = 0; row < scores.length; ++row) {
      for (int col = 0; col < scores[row].length; ++col) {
        scores[row][col] = 1 + rand.nextInt(100);
      }
    }
The formatted style of printing could be this:
    for (int row = 0; row < scores.length; ++row) {
      for (int col = 0; col < scores[row].length; ++col) {
        System.out.printf("%4d", scores[row][col]);
      }
      System.out.println("");
    }
Replacing the %4d format by %-4d left-aligns the columns.

We can use the enhanced for loop as the inner loop, which iterates values over each scores[row] array:
    for (int row = 0; row < scores.length; ++row) {
      for (int colscore : scores[row]) {
        System.out.printf("%4d", colscore);
      }
      System.out.println("");
    }
We can use the enhanced for loop for the outer loop as well, realizing that each element of scores is actually an array itself (the column array):
    for (int[] colarray : scores) {
      for (int colscore : colarray) {
        System.out.printf("%4d", colscore);
      }
      System.out.println("");
    }

Textbook example

Unlike regular arrays, 2-dimensional arrays are far less common in practice. The example from the textbook uses a 2-dimensional array to illustrate sales amounts:
by yearly quarter     (QTRS=4)
by corporate division (DIVS=3)
The array created is:
double[][] sales = new double[DIVS][QTRS];
Either quarter or division could be considered the "row". This example is VERY tedious to run because you have to enter 12 doubles through keyboard input.
CorpSales.java  

Initialized 2-dimensional array

As you would suspect, there is a way to achieve this ebd; just think of an array of arrays, e.g.,
TYPE [][] = {
  { ... },
  { ... },
  ...
};
Here is a small demo program to key in and run. The goal is to print out the array and do illustrate various summations on it.

Initialized2D.java
public class Initialized2D {
 
  public static void main(String[] args) {
 
    int[][] nums = { 
      { 2, 4, 6, 8 },
      { 1, 3, 5, 9 },
    };
 
    int total;
 
    // sum all elements in row 1
    total = 0;
    for (int col = 0; col < nums[1].length; ++col) {
      total += nums[1][col];
    }
    System.out.println("total in row 1: " + total);
 
    // sum all elements in column 2
    total = 0;
    for (int row = 0; row < nums.length; ++row) {
      total += nums[row][2];
    }
    System.out.println("total in column 2: " + total);
  }
 
}

Passing 2D arrays as parameters

There is nothing new here that has not already been done with regular arrays.

Ragged Arrays

A 2D array always has a fixed number of rows, but there column sizes within each row may vary. For example:
int[][] ragged = {
   { 2, 3, 4, 5 },
   { 6, 7 },
   { 8, 9, 10 },	
};
The textbook illustrates that ragged arrays can also be created dynamically.

Comparison of array usage

Going beyond access of a single array of a primitive data type, we have seen 3 types of arrays: Let us take a previous example of corresponding the name of a month to the numbers of days in that month.

Parallel arrays

As parallel arrays, we used:
String[] months = { 
	"January", "February", "March", "April", "May", "June", 
	"July", "August", "September", "October", "November", "December" 
};
 
int[] days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
With this version we match up the two corresponding features by:
months[i], days[i]
The problem is keeping correlated features in their correct positions and making sure the array sizes are equal. It is not a problem so much here because we know the data so well, but it does not "scale" well.

2D array

We could express the relation better as a 2D array, however a limitation of a 2D array is that the elements must all have the same type. So, we would want to write it as:
String[][] entries = {
  { "January", "31" },
  { "February", "28" },
  { "March", "31" },
  { "April", "30" },
  { "May", "31" },
  { "June", "30" },
  { "July", "31" },
  { "August", "31" },
  { "September", "30" },
  { "October", "31" },
  { "November", "30" },
  { "December", "31" },
};
In this way we could make the correlation of month to days via:
entries[i][0], entries[i][1]
The advantage over a parallel array is that the month is directly connected to the days. We can easily move pairs around, add them, delete them, etc. If we actually need the numerical value of the number of days, we could convert its string value to an integer.

Array of Objects

In my opinion, the arrays of objects, although more complex, is the hands-down winner. Here we use the helper class:

MonthNumDays

- String month
- int days

+ MonthNumDays(String, int)

+ String getMonth()
+ int getDays()

With this class, we express the month/day pairs in an array of objects:
MonthNumDays[] entries = {
  new MonthNumDays("January", 31),
  new MonthNumDays("February", 28),
  new MonthNumDays("March", 31),
  new MonthNumDays("April", 30),
  new MonthNumDays("May", 31),
  new MonthNumDays("June", 30),
  new MonthNumDays("July", 31),
  new MonthNumDays("August", 31),
  new MonthNumDays("September", 30),
  new MonthNumDays("October", 31),
  new MonthNumDays("November", 30),
  new MonthNumDays("December", 31),
};
A single month is correlated to its number of days by:
entries[i].getMonth(), entries[i].getDays()
This is far more transparent than expressing these features as columns 0 and 1 within the i-th row of a 2D array. Furthermore, we retain the correct type of the days entry.

3 or more dimensions

This is section 7.10 in the textbook. Ignore it.

Selection Sort and Binary Search

This is section 7.11 in the textbook.

These concepts are covered in Csc241, Data Structures. Ignore it.

Command-line arguments and variable-length argument lists

This is section 7.12 in the textbook.

Although command-line arguments are very important. running Java main classes through IDEs like NetBeans make it difficult to use command-line arguments, so ignore it.

Variable-length argument lists for methods, although useful, are rarely used, so ignore it.

The ArrayList class

An ArrayList is a Java Object which can create a list of other objects, in particular Strings. An ArrayList is based on a partially-filled array, but it provides all the support structure so that you never need to deal with the underlying array.

There are multiple problems with regular arrays, including: The syntax for declaring an ArrayList object of Strings is:
ArrayList<String> mylist = new ArrayList<>();   // Java 1.7+
Compare this to what the textbook dictates:
ArrayList<String> mylist = new ArrayList<String>();  // Java 1.6-
Both are "correct," but the former version is more modern. The syntax "<>" is called the diamond operator. The String type of the ArrayList object is inferred from the variable declaration.

The diamond operator will work only for Java 1.7 and above; nevertheless, you should probably use it since Java 1.7 is relatively "old" now. You'll see that NetBeans will flag the latter declaration; it's not an error but it is "old-fashioned."

Oddly enough, you can even drop the "generic part" completely:
ArrayList<String> mylist = new ArrayList();

ArrayList member functions

Given the following ArrayList of Strings,
ArrayList<String> mylist = new ArrayList<>();   // Java 1.7+
keep in mind that internally, mylist consists of 2 (private) data members:
String[] data;   // a partially-filled array
int size;        // the number of positions in use
Here are some of the public member functions:

Printing an ArrayList

The textbook briefly indicates the idea, but you can print the contents of an ArrayList directly via:
System.out.println(mylist);
If, say, we have:
ArrayList<String> mylist = new ArrayList<>();
mylist.add("AA");
mylist.add("BB");
mylist.add("CC");
System.out.println(mylist);
the output would be:
["AA", "BB", "CC"]
Effectively you can use mylist in any situation where you would print a string, e.g., as an argument used within a printf statement.

Being able to print objects is a vast improvement over an array, for which you would have to work much harder to achieve the same effect.

Textbook examples

ArrayListDemo1 creates a sample ArrayList, called nameList and illustrates some of the add, size, and get member function usages.
ArrayListDemo1.java  
ArrayListDemo2 illustrates iteration through an ArrayList using the enhanced for loop:
for (String name : nameList)
  System.out.println(name);
The enhanced for loop for arrays is only useful for full arrays, so this points to another improvement of ArrayList over an array.
ArrayListDemo2.java  
ArrayListDemo3 illustrates the positional remove operation.
ArrayListDemo3.java  
ArrayListDemo4 illustrates the positional add operation.
ArrayListDemo4.java  
ArrayListDemo5 illustrates the set operation:
ArrayListDemo5.java  

ArrayLists of Other Objects

Although the textbook does not deal with this at this point, but you can create ArrayLists of integers, doubles, etc., but you need to refer to the so-called wrapper classes:
Boolean    (boolean)
Integer    (int)
Double     (double)
Float      (float)
Long       (long)
Character  (char)
Byte       (byte)
Short      (short)
The texbook discusses these in section 9.6.

Let's work with the Integer class. An Integer, like a String is a reference. This assignment
Integer n = 5;
yields:
n: 
ref
5
This is why we say it is a wrapper; it is just an object surrounding a normal variable.

Autoboxing and Unboxing

In many ways Java can simply go back and forth between Integer and int as needed. Technically one creates an Integer via the new usage:
Integer n = new Integer(5);
It's like creating a String in a strict way:
String str = new String("Hello");
However, as you well know, String objects usage can bend the rules, i.e., we can simply do:
String str = "hello";
A similar thing is true of the wrapper classes, i.e., we can do:
Integer n = 5;
The type assignment is:
Integer = int
This procedure is called autoboxing; we're putting a simple int into an Integer "box." Going the other direction is also OK:
int m = n + 6;
Here we're doing this type assignment:
int = Integer + int
The procedure is called unboxing; we are taking n "out of the Integer box" and treating it like a regular int.

Integer ArrayList demo program

Here is a starter demo program using an Integer ArrayList.

IntArrayList.java
import java.util.ArrayList;
import java.util.Random;
 
public class IntArrayList {
 
  public static void main(String[] args) {
    ArrayList<Integer> intlist = new ArrayList<>();
 
    Random rand = new Random();
 
    for (int i = 0; i < 10; i++) {
      intlist.add(rand.nextInt(100) + 1);
    }
 
    System.out.println(intlist);
  }
}
There are many operations we can add to this basic format, like adding some number to each thing in the list, etc.

General objects

An ArrayList is very general in that it can hold any type of object. ArrayListDemo6 illustrates an ArrayList of BankAccounts. It relies on the class from Chapter 6:
BankAccount.java  
This is the textbook demo program:
ArrayListDemo6.java  
The ArrayList can be initialized like this:
ArrayList<BankAccount> list = new ArrayList<>();
An account is added to it like this:
list.add( new BankAccount(100.0) );
The balance of the account at index is gotten like this:
BankAccount account = list.get(index);
System.out.println( account.getBalance() );
If you are up to it, you can combine the two parts as:
System.out.println( list.get(index).getBalance() );
I recommend the 2-step approach instead of trying to be too succinct.


© Robert M. Kline