Return to lecture notes index
15-100 Lecture 21 (Monday, March 14, 2004)
Reviewing Arrays

class SortedNames {
  private String [] names;
  private int count;
  
  private static final int DEFAULT_MAXIMUM_NAMES = 10;
  
  public SortedNames (int maximumNames){
    names = new String[maximumNames]; //Create the array
    count = 0; //this will be keeping track of the number of names in the array
  }
  
  public SortedNames() {
    names = new String[DEFAULT_MAXIMUM_NAMES];
    count = 0;
  }
Inserting into Arrays (and Growing Them)
When you add a name the easiest place to insert it is at the back. The variable count is actually the first empty index. Thus it marks the back of your array and is the place that you should insert the new name. Why is this? Think about what count keeps track of...the number of names in the array. Since the index starts at zero (a fact you need to remember about arrays) the number of names in the array is actually one more than the index of the last name. Thus the referance at names[count] is null and names[count-1] has a name in it. Since names[count] is the first null referance this is where you should store the new name. Thus you store the new name in names[count] and then increment count by one (because the array now has one more referance stored in it).

Note: It is easy to forget to increment count. However doing so will cause your array to only have a name stored at index zero. So pay special attention to this short but important line.

But what if the array is full (that means that count==names.length)? Your program will throw a IndexOutOfBoundsException when you try to store the new name. Thus you need to grow the array before attempting to insert the next name. You can do this by writing a helper method to grow the array. (A helper method is method that contains code which a number of other class methods perform. Thus helper methods cut down on the amount of code writen and makes code easier to read. A helper method will only be called by other methods in the same class and thus helpers are private.) This helper should be called each time the array is full.

So how do you grow an array? Well technically you can't, arrays are a fixed size. However you can make a new array that is bigger and then copy the old array into the new array (keeping all objects in the correct position) and then make the referance (of the too small array)point to the new array. Thus the referance will point to a slightly larger array with more room. Now everything works...yea! And that is what matters. We don't have to stick with the same array as long as the referance points to any array which is big enough and contains the names in the correct order programmers don't care. We don't even have to worry about what happens to the old array (the Java grabage collector will take care of it).

Note: convention is that you double the size of your array. This is because growing an array (making a new array and then copying) takes a long time to do (relativly). If you are only growing by one each time you would have to grow each time you insert (very time intensive). If you grow by a fixed amount (say 10) then as you get into bigger arrays (say a million) then you arrive at a similar situation to when you were only growing by one. Doubling the array means that the bigger the array the more spaces you create. The minium loss of efficency (ie unused indexes) is 50% which isn't that bad. Thus doubling the array is an nice convention to follow when growing the array.

So here is the code 1)to insert a new name at the back of the array and 2)to grow an array:


  public void insert(String name){
    //first check to see if array is full, if so call helper method
    if (count == names.length){
      grow();
    }
    //now know the array is big enough to contain at least one more name.
		
    //names[count++] = name; does the same as below (since count increases after the new name has been stored)
    names[count] = name; //stores name in the array at position count 
    count++;             //increases count (so count is always the next free space in the array)
  }

  private void grow(){ 
    String[] biggerArray = new String[names.length * 2] //make a bigger array (to copy into)
    
    //now lets copy:
    for (int index = 0; index < names.length; index++) {
      biggerArray[index] = names[index];  //this copies the old array into the new array AT THE SAME POSITION
    }
    //biggerArray is now the exact same as names only it is twice as big.
    
    //now move the referance...
    names = biggerArray;
  }
Sorting Arrays (and Swapping Stored Values)
The best way to keep a sorted array is to insert them into the array in order. We want to keep the names in alphabetical order. How do we do this? Well when we add the first name then the array is already in alphabetical order (there's only one name in it). On second add drop the new name at back of array. Then look at the name in front of it (index: count-2, since count increased when we added). If the list of two is in order do nothing. If they are in reverse order switch the two names (calling the helper method described in the next paragraph). The list is now in order. This works for all nth adds. (Run the 3rd add through your head: you drop the new name at the back. If the array is now out of order then swap the new value with the value one in front of it until the list is in order. At each comparison you will either have to swap or the new name will be in the correct position and you can exit the loop or new name will be stored at index zero and you should exit th loop to avoid an IndexOutOfBoundsException). In this way the array is kept in order. Notice that since the array is ordered, once you find a value that belongs in front of the new name you have found the "correct spot" for newName and you don't have to look any further forward in the array because the rest of the array is ordered and there is no way that after the correct spot there is another name ahead of the spot that actually belongs behind the new value. Note: To do this swapping/ ordering process you need to start at the end of the array and work towards the 0th index.

Here's an example of adding i to the ordered list "g h j k":

      g h j k i //insert i at the end, compare k and i (need to swap)
      g h j i k //compare i and j (need to swap)
      g h i j k //compare i and h (don't need to swap)
  no more changes will happen so don't have to continue the comparisons/ swapping

Swapping values (a helper method): so basically you need to swap the names at two different indexes. Why is the below code wrong?


    names[x] = names [y];
    names[y] = names [x];
Because now both names[x] and names [y] will hold a referance to the same value! So need to use a three way swap (involves a temporary variable):

      x   y   temp
      A   B    A
      B  temp (A)
      B   A    A
Here's the code for inserting in order and the swap method (takes parameters of the two indexes to swap):

  public void insertInOrder(String name){
    if (count == names.length){
      grow();
    }
    
    names[count] = name;  
    count++;             
    
    for (int index = (count-1); index > 0; index--){ //can't let index be equal to zero because we are doing (index-1) and always looking ahead by one.
      if (names[index].compareTo(names[index-1]) > 0) { //this will be true if the array is out of order (remeber
                                //how compareTo works)
        swap(index, index-1);
      }
      
      //put this in to avoid extra work...if you don't need to swap then name is 
			//in the correct spot and you can exit the loop
      else {
        break;
      }
      
      /*same as: 
       *if(names[index].compareTo(names[index-1]) <= 0){ 
       *  break;
       *}  
       *swap(index, index-1);
       */
    }
  }
	
  public void swap(int x, int y) {
    
    String temp = names [y];
    names[x] = names [y];
    names[y] = temp;
    
  }
However, a better solution is to avoid all of the swaping and copying. Just make a hole where name should go. If you arn't in the right spot move the hole forward and step forward until you've found the "correct spot" then just copy name into there. Like this:
  Ordered list: 2 4 6 8
  Number to insert: 3

  Moving the hole:

    2 4 6 8 _  //hole starts at back, compare 3 and 8 (need to move hole)
    2 4 6 _ 8  //compare 3 and 6 (need to move 6 back and hole forward)
    2 4 _ 6 8  //compare 3 and 4 (need to move hole)
    2 _ 4 6 8  //compare 3 and 2 (you have found the correct spot!)
    2 3 4 6 8  //insert 3 into the created hole and exit
So let's rewrite the insertInOrder():

  public void insertInOrder(String name){
    if (count == names.length){
      grow();
    }
    
    int index = count;
    //in loop while not at end of array and yavn't found correct spot
    while ((index > 0) && (names[indx-1].compareTo(name)) > 0) {
      names[index] = names[index-1];
      names[index - 1] = null;
      index++;
    }
		
    //once exit while loop know index is where name should be (insert it)
    names[index] = name;
    count++;
  }
}