CSC383 Jan09

slide version

single file version

Contents

  1. Checked Exceptions
  2. Unchecked Exceptions
  3. Errors
  4. Exception and Error class Hierarchy
  5. Guidelines for using Java Exceptions
  6. Scanner and Regular Expression Patterns
  7. Java Regular Expression Notation
  8. Regular Expression Multipliers
  9. Read CSV File
  10. Application - Filter
  11. Storing Data
  12. Linear Search
  13. Binary Search
  14. Client Filter - Version I
  15. Client Filter - Version II
  16. Using an Abstract Data Type - SetOfInts
  17. Client Filter - Version III
  18. Stack Abstract Data Type
  19. Stack UML Diagram
  20. Method Documentation
  21. Implementation

Checked Exceptions[1] [top]

Checked exceptions generally corresond to problems that are not programmer errors.

A typical example is a program that prompts a user for an input file name. The programmer cannot prevent the user from typing an incorrect file name. FileNotFoundException, a checked exception, then results when an attempting to open the file.

A program should be prepared to catch a checked exception and possibly attempt some recovery or at the least to exit gracefully.

In the example, the program could report the invalid file name and prompt the user to try again.

Java language requires that a method that makes calls that can throw a checked exception must catch the exception or else declare that it will (indirectly) cause the checked exceptionto be thrown.

However, this deferral of catching exceptions should not include the main method of a finished application.

Unchecked Exceptions[2] [top]

Unchecked exceptions on the other hand should not occur if the program has been written carefully. Examples include passing inappropriate values to methods, calling methods when the state of the instance forbids it, and accessing arrays with a subscript out of bounds.

Generally a class will provide ways of checking any preconditions for calling its methods and so a program can be written to ensure that unchecked exceptions will not occur.

Errors[3] [top]

Error classes can also be thrown, but are not derived from Exception.

Error types are also unchecked (but they are unchecked Errors, not unchecked Exceptions).

Errors and unchecked exceptions are both unchecked throwables.

The convention is that errors should only be thrown by the java virtual machine to indicate conditions that make it impossible to continue (e.g. disk failure, stack overflow, etc.)

User class methods should not throw Error or any of its derived class types.

Exception and Error class Hierarchy[4] [top]

      Throwable
      |
      +-------+------------------+
      |                          |
      Exception                  Error
      |                          |
      +------------------+       unchecked classes...
      |                  |
      RunTimeException   checked classes ...
      |
      unchecked classes
    

Guidelines for using Java Exceptions[5] [top]

  1. Identify preconditions for the methods in classes you write.
  2. In each method check if its preconditions are met. If not, throw an appropriate exception.
  3. Include methods in your class that lets the user of the class test whether preconditions regarding the state of a class instance are met.
  4. Stick to the standard Java exception classes if possible. Usually you can find one that is appropriate for your purposes without having to create new Exception classes.

    A short list of commonly used java unchecked exceptions:

    1. IndexOutOfBoundsException (unchecked)
    2. IllegalArgumentException (unchecked)
    3. IllegalStateException (unchecked)
    4. NoSuchElementException (unchecked)
    5. UnsupportedOperationException (unchecked)

Scanner and Regular Expression Patterns[6] [top]

The Scanner next() method reads the next word.

But the default meaning of word is whitespace delimited sequence of input characters.

The delimiter pattern can be changed using Scanner's useDelimiter method.

The argument to useDelimiter should be string representing a pattern. Then any sequences of characters that match the pattern will be treated as the delimiter to use in determining the words by the Scanner.

Patterns are spcified by regular expressions

Java Regular Expression Notation[7] [top]

Characters

      \t           The tab character 		    
      \n 	   The newline (line feed) character  
      \r 	   The carriage-return character 	    
      \f 	   The form-feed character 	    
      \a 	   The alert (bell) character 	    
      \e 	   The escape character               
    

Character Classes

      [abc]        a, b, or c (simple class)
      [^abc]       Any character except a, b, or c (negation)
      [a-zA-Z]     a through z or A through Z, inclusive (range)
    

Predefined Character Classes

      .            Any character (may or may not match line terminators)
      \d 	   A digit: [0-9]					      
      \D 	   A non-digit: [^0-9]				      
      \s 	   A whitespace character: [ \t\n\x0B\f\r]	      
      \S 	   A non-whitespace character: [^\s]		      
      \w 	   A word character: [a-zA-Z_0-9]			      
      \W 	   A non-word character: [^\w]                          
    

Warning: \ is used as an escape for characters; that is, '\n' or "\n" doesn't mean the character n. The usual meaning of character n is "escaped" and '\n' has the special meaning: the newline character.

The predefined character classes are not single characters, so this interfers with the use of \ as a escape character. That is, \s has no special meaning as a single character. The way Java deals with this is to escape the \ inside a string:

      "n"   string with 1 n
      "\n"  string with 1 newline
      "s"   string with 1 s
      "\s"  Error \s is an illegal escaped character
      "\\s" String indicating the character class of any whitespace character

    

Regular Expression Multipliers[8] [top]

How do you write a pattern that matches 1, 2, 3, or more whitespace characters?

Answer: Use a multiplier:

A * following a regular expression means 0 or more occurrences.

A + following a regular expression means 1 or more occurrences.

So,

      "\\s*,\\s*"    matches 0 or more whitespace characters followed by
                     a comma, followed by 0 or more whitespace
                     characters.
      "[ \r\t\n]+"   (What does this match?)
      "[\\s,.!?]+"   (What does this match?)
    

Read CSV File[9] [top]


import java.util.Scanner;

public class ReadCSV
{
  public static void main(String[] args)
  {
    Scanner in = null;

    try {
       in = new Scanner(new File(args[0]));
    } catch(FileNotFoundException e) {
       System.out.printf("Unable to open input file %s\n", args[0]);
       System.exit(0);
    }
  
    String[] fields;
    String line;
    while( in.hasNextLine() ) {
      line = in.nextLine();
      fields = line.split("\\s*,\\s*");
      for(int i = 0; i < fields.length; i++) {
        System.out.printf("field %d: %s\n", i, fields[i]);
      }
    }
  }
}

Application - Filter[10] [top]

Input items should be compared to a allowed items. Display the input items not in the allowed list.

For each input item a search is required to see if it is in the allowed list.

How are the allowed items to be stored?

How will the allowed items be searched for each input item?

Storing Data[11] [top]

Allowed items could be stored in an ordinary array. How big should the array be?

Alternatively items could be stored in an ArrayList.

In this example, assume for the moment that the allowed list is stored in an array and will not be modified.

Linear Search[12] [top]

Linear search of an array of keys for an item examines each array element until the item is found or until all array elements have been compared with no match.

For examle, assuming the keys are of type int, this rank function returns the array index where item is found or -1 if not found.


public int rank(int[] keys, int item)
{
  for(int i = 0; i < keys.length; i++) {
      if ( item == keys[i] ) {
         return i;
      }
  }
  return -1;
}

Binary Search[13] [top]

Instead of examining every array element (e.g., when item is not found), binary search will examine many fewer items.

For example, if there are 1024 keys, linear search for an item not in the array will examine all 1024 keys. Binary search, will examine only about 10 keys.

However, for the array must be sorted!


public int rank(int[] keys, int item)
{
  int lo = 0;
  int hi = keys.length - 1;

  while(lo <= hi) {
    int mid = (lo + hi) / 2;
    if ( keys[mid] == item ) {
      return mid;
    } else if ( item < keys[mid] ) {
      hi = mid - 1;
    } else {
      lo = mid + 1;
    }
  }
  return -1;
}

Client Filter - Version I[14] [top]

This version


public class LinearSearch
{
   private static int rank(int[] keys, int item)
   {
     // linear search implementation above
     ...
   }

   public static void main(String[] args)
   {
     int[] keys = StdIn.readInts(args[0]);
     
     while(!StdIn.isEmpty()) {
       int item = StdIn.readInt();
       if ( rank(keys, item) == - 1 ) {
          System.out.println(item);
       }
     }
   }
}

Client Filter - Version II[15] [top]

This version is similar, but uses binary search.


import java.util.Arrays;

public class BinarySearch
{
   private static int rank(int[] keys, int item)
   {
     // binary search implementation above
     ...
   }

   public static void main(String[] args)
   {
     int[] keys = StdIn.readInts(args[0]);

     Arrays.sort(keys);

     while(!StdIn.isEmpty()) {
       int item = StdIn.readInt();
       if ( rank(keys, item) == - 1 ) {
          System.out.println(item);
       }
     }
   }
}

Using an Abstract Data Type - SetOfInts[16] [top]

The client code above is not too complicated, but even in this case, can be made clearer and less dependent on the implementation (i.e., the data representation and/or the search implementation).

USe a SetOfInts class that stores the data from an array of keys and provides a public contains method.


public class SetOfInts
{
  private int[] keys;

  public SetOfInts(int[] a)
  {
    keys = a;
    Arrays.sort(keys);
  }

  private int rank(int item)
  {
    // binary search version of rank
    ...
  }
  
  public boolean contains(item)
  {
    return rank(item) != -1;
  }
}

Client Filter - Version III[17] [top]

This version uses the SetOfInts ADT.


public class Filter
{
  public static void main(String[] args)
  {
    int[] keys = StdIn.readInts(args[0]);
    SetOfInts s = new SetOfInts(keys);

    while(!StdIn.isEmpty()) {
      int item = Stdin.readInt();
      if (!s.contains(item) ) {
         System.out.println(item);
      }
    }
  }
}

Stack Abstract Data Type[18] [top]

A stack type should allow insertions and deletions such that the item deleted is the last one inserted - last in, first out.

We might also want to know if the stack is empty and if not, to be able to examine the last item inserted.

Stack UML Diagram[19] [top]

The data type should be stored in the stack will not affect the stack operations.

So use a generic type variable, say E, for this data.

The UML diagram below gives the syntactic specification of the Stack class, but nothing about the actual meaning; i.e., the last-in, first-out property.

What is needed is additional documentation of the method properties.

Stack<E>
.. private data ..
+Stack()
+void push(E x)
+E pop()
+E peek()
+boolean isEmpty()
+int size()

Method Documentation [20] [top]


/**
 * The Stack class represents a last-in, first-out data type.
 * 
 * @author glancast 
 */
public class Stack<E>
{
  /**
   * Creates an empty Stack
   *
   */
  public Stack()
  {}

  /**
   * Inserts an item on the top of this Stack.
   *
   * @param x the item to be inserted
   */
  public void push(E x)
  {}
  
  /**
   * Removes the last inserted item on the stack
   * and returns that object as the return value of
   * this method.
   *
   * @return The item removed from the top of the stack.
   * @throws NoSuchElementException if the stack is empty.
   */
  public E pop()
  {}
  
  /**
   * Returns the last inserted item on the the stack without 
   * removing it.
   *
   * @return The item at the top of the stack.
   * @throws NoSuchElementException if the stack is empty.
   */
  public E peek()
  {}
  
  /**
   * Test if this stack is empty.
   * @return true if the stack is empty; false, otherwise.
   */
  public boolean isEmpty()
  {}

  /**
   * Get the number of elements in this stack.
   *
   * @return the number of elements
   */
  public int size()
  {}
}

Implementation[21] [top]

We could use an array (with elements of the generic element type E) to store the elements of the stack.

But an array is fixed size, so it doesn't actually grow or shrink as items are inserted or deleted.

We could keep track of two things:

  1. Actual size of the array - capacity.
  2. The number of items logically inserted in the array - sz.

Then,

This can work, but the major problem is the size of the array. The push method cannot do its job if the array is full!

This can be overcome by using an ArrayList instead of an ordinary array since an ArrayList grows as needed when elements are added.