Abstract Data Types (ADTs) and data structures: terminology and definitions A type is a collection of values. For example, the boolean type consists of two values, true and false; the byte type consists of all integers between -127 and +128. A data type is a type + a set of operations on data of the type. For example, integers of type byte plus {+, -, *, /} comprise the data type byte. An integer array is a data type which has assign and search operations associated with it. An abstract data type is a data type solely defined in terms of a type and a set of operations on that type. Each operation is defined in terms of its input and output without specifying how the data type is implemented. A stack is an example of an ADT, defined in terms of push and pop operations. A data structure is an implementation of an ADT. The Stack ADT, for example, an be implemented by means of an array. Levels of abstraction in data specification Any data can be defined in two formats: logical and physical. An ADT defines data in a logical format -- as a conceptual model, which is completely independent of its implementation. Consider, for example, a matrix of integers. At a logical level, we are not interested in how this matrix is implemented. The only items of interest are what type of data this matrix consists of, and what operations will be performed on this data. At a physical level, the associated data structure representing the Matrix ADT can be implemented with a different degree of abstraction by means of: – – – a high level language, in which case the Matrix ADT can be defined as an array of integers. in assembly language (or byte code) it is represented as a sequence of words in computer memory. in machine language it is represented as a sequence of electronic bits. The Matrix example (cont.) The ADT Matrix, in more formal terms, can de defined as a collection of data items of type MatrixData arranged as a rectangular grid, and associated with the following set of operations: – – Retrieve (M, row#, col#) Assign (M, row#, col#, value) To implement the ADT matrix, we can use a two-dimensional array. For example: int[ ][ ] M = {{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}, {11, 12, 13, 14, 15}} The retrieve and assign operations can be implemented as follows: item = M[1][2] M[2][3] = item Representation of two-dimensional arrays in computer memory Computer memory is a linear sequence of memory cells. There are two ways to translate a two-dimensional array into a linear sequence: 1. Row by row (this is called the row-major form). To implement assign and retrieve operations, we must be able to find a particular entry. This can be done by means of the following formula (called a mapping function): (# of columns * (i - 1) + j), where i is the specified row#, and j is the specified col#. 2. Column by column (this is called the column-major form) in which case the mapping function is (# of rows * (j - 1) + i). The Stack ADT Definition A stack is a restricted list in which entries are added and removed from the same end, called the top. This strategy is known as last-in-first-out (LIFO) strategy. Operations (methods) on stacks: push (item) pop () size () empty () full() ontop() Inserts item on the top of the stack Removes the top item Returns the number of items in the stack Returns true if the stack is empty Returns true if the stack is full Returns the top element without removing it from the stack There is a built-in class, Stack, in the java.util package, but often it is desirable to have your own implementation. Next, we discuss a specific implementation of the Stack ADT. The Stack Interface in two versions version 1: version 2: public interface Stack { public interface Stack { public void push (int item); public int pop(); public int size(); public boolean empty(); public boolean full(); public int ontop(); public void push (int item) throws StackFullException; public int pop() throws StackEmptyException; public int size(); public boolean empty(); public boolean full(); public int ontop() throws StackEmptyException; } } The Stack ADT -- an array implementation (version 1) class StackADT implements Stack { final int MAXSIZE = 100; private int size; private int[] stackADT; private int top = -1; public StackADT () { size = MAXSIZE; stackADT = new int[size]; } public int pop () { int i = stackADT[top]; top--; return i; } public boolean empty () { return (top == -1); } public boolean full () { return (top == size - 1); } public StackADT (int inputsize) { size = inputsize; stackADT = new int[size]; } public int ontop () { int i = pop(); push(i); return i; } public void push (int number) { top++; stackADT[top] = number; } public int size () { return (top + 1); } } A note on JAVA exceptions Exceptions are events occurring during program execution which make continuation impossible or undesirable. Examples of exceptions: arithmetic overflow, array reference with index out of bounds, invalid user entry, etc. When an exception occurs, an exception handler is automatically invoked to resolve the problem or terminate the program in a controlled manner. There are two kinds of exceptions in JAVA: – Implicit (built-in) exceptions which are signals from the Java Virtual Machine to the program indicating a violation of a semantic constraint of the Java language. Example: an attempt to index outside the array’s bounds will automatically throw an ArrayIndexOutOfBoundsException. – Explicit exceptions which are intended to capture possible errors anticipated by the program designer. Example: user input outside the specified range. To define such an exception, a new exception class may have to be defined. Defining new exception classes Version 2 of the Stack ADT implementation requires the following two exception classes to be defined: class StackEmptyException extends Exception { public StackEmptyException (String message) { System.out.println (message); } } class StackFullException extends Exception { public StackFullException (String message) { System.out.println (message); } } To throw an exception, we must create a new object of the exception’s type, i.e: throw new StackFullException (“The stack is full.”); The string serving as a parameter for exception constructor must be displayed by the corresponding catch statement, i.e. catch (StackFullException exception) { showStatus (exception.toString()); } The Stack ADT -- an array implementation (version 2) class StackEmptyException extends Exception { public StackEmptyException (String message) { System.out.println (message); }} class StackFullException extends Exception { public StackFullException (String message) { System.out.println (message); }} class StackADT implements Stack { final int MAXSIZE = 100; private int size; private int[] stackADT; private int top = -1; public void push (int number) throws StackFullException { if (size() == size) throw new StackFullException ("The stack is full."); top++; stackADT[top] = number; } public int pop () throws StackEmptyException { if (empty()) throw new StackEmptyException ("The stack is empty."); int i = stackADT[top]; top--; return i; } public int ontop () throws StackEmptyException { if (empty()) throw new StackEmptyException ("The stack is empty."); int i = pop(); try { push(i); } catch (StackFullException e) { System.out.println ("The stack is full."); } return i; } ............ // size, empty and full methods follow public StackADT () { size = MAXSIZE; stackADT = new int[size]; } public StackADT (int inputsize) { size = inputsize; stackADT = new int[size]; } } Example application of the Stack ADT using version 1 class StackAppl { public static void main (String[] args) throws IOException { Scanner scan = new Scanner(System.in); System.out.print ("Enter stack size: "); System.out.flush(); int size = scan.nextInt(); StackADT stack = new StackADT(size); int i = 2; while (!stack.full()) { stack.push(i); System.out.println (stack.ontop() + " is the top element."); i = i + 2; } System.out.println ("The current stack contains " + stack.size() + " elements."); while (!stack.empty()) System.out.println (stack.pop() + " is removed from the stack."); if (stack.empty()) System.out.println ("The stack is empty."); else System.out.println ("There are more elements on the stack."); } } Example application of the Stack ADT using version 2 class StackAppl2 { public static void main (String[] args) throws IOException { Scanner scan = new Scanner(System.in); System.out.print ("Enter stack size: "); System.out.flush(); int size = scan.nextInt(); StackADTv2 stack = new StackADTv2(size); int i = 2; try { // two exceptions, StackFullException and for (int j = 1; j <= 7; j++) { // StackEmptyException, must be watched for stack.push(i); System.out.println (stack.ontop() + " pushed"); i = i + 2; } } catch (StackFullException e) { System.out.println ("The stack is full."); } catch (StackEmptyException e) { System.out.println ("The stack is empty."); } System.out.println ("The current stack contains " + stack.size() + " elements.") try { for (int j = 1; j <= 7; j++) { System.out.println (stack.pop() + " poped"); }} catch (StackEmptyException e) { System.out.println ("The stack is empty."); } } }