Return to lecture notes index

15-100 Lecture 25 (Wednesday, November 2, 2005)

Quick Quiz

// Write a code segment only -- no method or class required.

// You must store and retrieve the numbers from the array
// ...don't just print the loop control variable!

// Store the first 20 non-negative integers within an array

int[] numbers;
numbers = new int[20];

for (int index=0; index < 20; index++)
  numbers[index] = index;


// Print out the even numbers from this array 

for (int index=0; index < 20; index+=2)
  System.out.println (numbers[index]);

Today's Example

Today, we're going to create a generic Container based on an array of objects. The idea is that we'll be able to add and remove things from this Container, as well as to determine if an Object is already within the container.

The example serves to illustrate the use of arrays -- both their syntax and sematics, and also how they can be used in practice.

The Storage

Storage within the Container is based on an array of Objects. We choose to create an array of Objects, becuase it enables this Container to hold anything, because all objects are derived from Object and all primitives can be wrapped within Objects (more on that soon).

We initialize the array when the Container is created, via the constructor. But, how big should it be? We'll answer that question two ways:

...this way, we can accomodate the expected common cases -- and also much larger cases.

We also keep an instance variable, nextSlot, that serves to tell us the next available slot in the array -- in other words, where we put the next item. Since the array's first index is 0, this variable also contains the count of items -- it is initially 0 and remains one ahead of the most recently added item.

In creating the class, we'll use a "final" variable to set the default size. Since this is a definition, not a property of an instance, we make it "static". This type of variable is known as a "configuration constant". This is because it can be changed, a.k.a., configured, but only before the program is compiled, hence "constant" during the program's execution.

class Containter {

  private Object[] list;
  private int nextSlot;
  
  private static final int DEFAULT_SIZE = 10;
  

  public Container() {
    list = new Object[DEFAULT_SIZE];
    nextSlot = 0;
  }
  
  
  public Container (int size) {
    list = new Object[size];
    nextSlot = 0;
  }

}

toString()

At this point, we can write toString(). Doing so will enable us to test, our constructors, at least in a trivial way, and also enable us to test everything else we write.

The toString() will return a String containing each item within the Container, one per line. Instead of using a "\n" to represent a newline, I'll ask the System for the new line string. I do this becuase different environments can choose different Strings. For example, UNIX systems use only a "\n", literally "newline", whereas Windows systems use "\r\n", literally "carriage return, new line".

Notice I'm adding another configuration constant at the top. Notice the use of the System.getproperty() method:

Beyond the small detail, the overall strategy is a simply traversal of the array. We walk through each item from the 0th through the last one, the one before nextSlot, and add each to the String that we'll eventually return. After we add each, we add a new line.

  private static final int DEFAULT_SIZE = 10;
  private static final String NL = System.getProperty ("line_separator");

  ...

  public String toString() {
  
    String rep = "";
    
    for (int index=0; index < nextSlot; index++) {
      rep += items[index] + NL;
    }
  }

The add(...) Method

The add method is, in principle, very straight-forward. We have our state variable, nextSlot, that maintains the position of the next available slot. We drop our item off into this slot and then advance the nextSlot counter.

About the only thing that can go wrong is that the array is already full. If the array is already full, (nextSlot == list.length). This is because an array of length list.length has indexes [0...(list.length-1)] -- but not list.length. Should we try to access an element outside of the legal bounds, an ArrayIndexOutOfBoundsException will be thrown. This is an "unreported" exception, so it doesn't need to be "declared thrown", but it certainly can and will arise.

Next class, we'll learn to make our array's grow in response to demand. But, for today's class, we'll just take advantage of this Exception and return false, should it occur.

Notice that after adding an item, we bump nextSlot forward -- this gets us ready to to it again for the next one.

  public boolean add (Object item) {
 
   try {
     list[nextSlot++] = item;
     return true;
   } 
   catch (ArrayIndexOutOfBoundsException aioobe) {
     return false;
   }
  }

The contains(...) Method

The contains(...) method is really an extension of the same traveral we used for toString(). We walk through each item within the Collection, from [0...(.length-1)]. The only difference is that, for each item, we check to see if it matches the one for which we are searching using equals(). If it does, we immediately return true -- we are done, there is no reason to continue the traversal. If however we work our way through the entire array and don't find a match, we return false -- there is no where else to look.

  public boolean contains (Object item) {
  
    for (int index=0; index < nextSlot; index++) {
      if (items[index].equals(item)) 
        return true;
    }
    
    return false;
  
  }

Container, so far

class Containter {

  private Object[] list;
  private int nextSlot;
  
  private static final int DEFAULT_SIZE = 10;
  private static final String NL = System.getProperty ("line_separator");
  

  public Container() {
    list = new Object[DEFAULT_SIZE];
    nextSlot = 0;
  }
  
  
  public Container (int size) {
    list = new Object[size];
    nextSlot = 0;
  }
  

  public boolean add (Object item) {
 
   try {
     list[nextSlot++] = item;
     return true;
   } 
   catch (ArrayIndexOutOfBoundsException aioobe) {
     return false;
   }
  }
  
  
  public boolean contains (Object item) {
  
    for (int index=0; index < nextSlot; index++) {
      if (items[index].equals(item)) 
        return true;
    }
    
    return false;
  
  }
  
  public String toString() {
  
    String rep = "";
    
    for (int index=0; index < nextSlot; index++) {
      rep += items[index] + NL;
    }
  }

}

Next Class

We'll take a look at how to make the Container's underlying array grow in response to demand. We'll also look at removing elements.