680 likes | 704 Views
Chapter 14: Stacks. Stacks. This chapter explores the idea of collections in general and introduces one classic collection: a stack Chapter 14 focuses on the concept of a collection a classic collection: stacks generic types and their use in collection classes
E N D
Stacks • This chapter explores the idea of collections in general and introduces one classic collection: a stack • Chapter 14 focuses on • the concept of a collection • a classic collection: stacks • generic types and their use in collection classes • fixed (arrays) and dynamic (linked lists) structures • stack implementations • the Java Collections API
Outline • Introduction to Collections • A Stack Collection • Generics • Using Stacks: Postfix Expressions • Implementing a Stack: with Arrays • References as Links • Implementing a Stack: with Links • Packages
14.1 – Introduction to Collections • We are used to collections of items: stamps, CDs, keys, etc. • A collection is an object that serves as a repository for other objects • A collection provides services to add, remove, and manage the elements it contains • The underlying data structure used to implement the collection is independent of the operations provided
14.1 – Introduction to Collections • Collections can be separated into two categories • linear: elements are organized in a straight line • nonlinear: elements are organized in something other than a straight line • Organization elements, relative to each other, is usually determined by • the order in which they were added to the collection • some inherent relationship among the elements
14.1 – Separating Interface from Implementation • An abstract data type (ADT) is a set of data and the particular operations that are allowed on that data • An ADT is considered abstract because the operations you can perform on it are separated from the underlying implementation • A collection • is an abstract data type • defines interface operations through which the user can manage the objects in the collection, such as adding / removing elements
14.1 – Separating Interface from Implementation • The details of how a collection is implemented to fulfill that definition should be an independent issue • For every collection we examine, we should consider • How does the collection operate, conceptually? • What operations are included in the interface to the collection? • What kinds of problems does the collection help us solve? • How might the collection be implemented? • How do the implementations compare from an efficiency point of view?
14.1 – Separating Interface from Implementation Class thatimplementsthe collection Services provided by collection that adhere to an interface Interface to collection Class that usesthe collection
14.1 – The Java Collections API An API (Application Programming Interface) is a set of classes that support some particular aspect of software development The Java Collections API is a set of classes that represent collections, implemented in various ways As we explore collections, we will examine both our own implementations and look at what support is provided by the Java Collections API
Outline • Introduction to Collections • A Stack Collection • Generics • Using Stacks: Postfix Expressions • Implementing a Stack: with Arrays • References as Links • Implementing a Stack: with Links • Packages
14.2 – Stacks • A stack is a linear collection whose elements are added and removed from the same end • Stacks are processed in a last in, first out (LIFO) manner • Usually, stacks are depicted vertically, and we refer to the top of the stack as the end to which elements are added and removed
14.2 – Stacks Adding an element Removingan element Top of stack
Outline • Introduction to Collections • A Stack Collection • Generics • Using Stacks: Postfix Expressions • Implementing a Stack: with Arrays • References as Links • Implementing a Stack: with Links • Packages
14.3 - Generics Chapters 8 and 9 discussed the object-oriented concepts of inheritance and polymorphism They are based on rules of type compatibility which lead to type checking done by the compiler An Object reference can refer to any kind of object, because of inheritance But that is a little too forgiving, so Java also provides a more formal way to establish such a relationship: generics
14.3 – Generic Types • A class can be based on a generic type • The class will then be able to operate on, store, and manage objects whose type is not specified until the class is instantiated • Example: suppose we wanted to define a class called Group that stores and manages a group of objects. • Using polymorphism, we can informally make Group generic by having it store references to Object
14.3 – Generic Types • However, any type of object could then be stored in our Group, resulting in a loss of control • A better approach is to define the Group to store a generic type T as follows class Group<T> { // declarations and code that manages objects of type T } • T is just a placeholder; you can use any identifier • The actual class that T represents is delayed until you instantiate a Group object
14.3 – Generic Types • Instantiating a Groupof Productobjects: Group<Product> group1 = new Group<Product>(); • Instantiating a Groupof Friendobjects: Group<Friend> group2 = new Group<Friend>(); • Only objects compatible with the Productclass (a Productor any of its descendents) can be stored in group1 • This is checked by the compiler, providing better type safety
14.4 – A Stack ADT Generic types and containers go hand in hand A container class can be defined to operate on a generic type T, and then each instantiation of that collection can be used to manage specific types of objects Interfaces can be defined with generic types, too The Stack<T> interface on the next slide defines an abstract data type (ADT) for a stack
14.4 – A Stack Interface //******************************************************************** // Stack.java Java Foundations // // Defines the interface to a stack collection. //******************************************************************** package javafoundations; public interface Stack<T> { // Adds the specified element to the top of the stack. public void push (T element); // Removes and returns the top element from the stack. public T pop(); // Returns a reference to the top element of this stack // without removing it. public T peek(); // Returns true if the stack contains no elements and false // otherwise. public boolean isEmpty(); // Returns the number of elements in the stack. public int size(); // Returns a string representation of the stack. public String toString(); }
Outline • Introduction to Collections • A Stack Collection • Generics • Using Stacks: Postfix Expressions • Implementing a Stack: with Arrays • References as Links • Implementing a Stack: with Links • Packages
14.5 – Evaluating Postfix Expressions • Before looking further at the implementation of a stack, let's first see how one might be used • Arithmetic operations are traditionally written in infix notation, meaning that the operator is placed between its operands in the form <operand> <operator> <operand> • When evaluating infix expressions, we rely on precedence rules to determine the order of operator evaluation • In a postfix expression, the operator comes after its two operands <operand> <operand> <operator>
14.5 – Evaluating Postfix Expressions • The process of evaluating a postfix expression can be stated simply: • scan from left to right, • apply each operation to the two previous operands immediately preceding it and • replace the operator with the result • Consider the infix expression: 4 + 5 * 2 • In postfix notation, it would be written as: 4 5 2 * +
14.5 – Evaluating Postfix Expressions • Consider the design of a program that can compute the result of a postfix expression • The evaluation rule relies on being able to retrieve the previous two operands whenever we encounter an operator • A large postfix expression will have many operators and operands to manage • A stack is the perfect collection to use in this solution
14.5 – Evaluating Postfix Expressions • Solution algorithm: • scan the expression from left to right, identifying each token as an operator or operand • if the scanned token is an operand, push it onto the stack • if the scanned token is an operator • pop the top two elements off the stack, • apply the operation to them, and • push the result onto the stack • If we reach the end of the expression the remaining element on the stack is the result of the expression (otherwise the expression was not well formed)
14.5 – Using a Stack to Evaluate a Postfix Expression Given the expression: 7 4 -3 * 1 5 + / * + * 5 / 1 6 * -3 -2 -12 4 top 7 -14
14.5 – Postfix.java //******************************************************************** // Postfix.java Java Foundations // // Demonstrates the use of a stack to evaluate postfix expressions. //******************************************************************** import java.util.Scanner; public class Postfix { //------------------------------------------------------------------ // Reads and evaluates multiple postfix expressions. //------------------------------------------------------------------ public static void main (String[] args) { String expression, again; int result; try { Scanner in = new Scanner(System.in); (more…)
14.5 – Postfix.java do { PostfixEvaluator evaluator = new PostfixEvaluator(); System.out.println ("Enter a valid postfix expression: "); expression = in.nextLine(); result = evaluator.evaluate (expression); System.out.println(); System.out.println ("That expression equals " + result); System.out.print ("Evaluate another expression [Y/N]? "); again = in.nextLine(); System.out.println(); } while (again.equalsIgnoreCase("y")); } catch (Exception IOException) { System.out.println("Input exception reported"); } } }
14.5 – PostfixEvaluator.java //******************************************************************** // PostfixEvaluator.java Java Foundations // // Represents an integer evaluator of postfix expressions. Assumes // the operands are constants. //******************************************************************** import javafoundations.LinkedStack; import java.util.Scanner; public class PostfixEvaluator { private final char ADD = '+', SUBTRACT = '-'; private final char MULTIPLY = '*', DIVIDE = '/'; private LinkedStack<Integer> stack; //------------------------------------------------------------------ // Sets up this evalutor by creating a new stack. //------------------------------------------------------------------ public PostfixEvaluator() { stack = new LinkedStack<Integer>(); } (more…)
14.5 – PostfixEvaluator.java //------------------------------------------------------------------ // Evaluates the specified postfix expression. If an operand is // encountered, it is pushed onto the stack. If an operator is // encountered, two operands are popped, the operation is // evaluated, and the result is pushed onto the stack. //------------------------------------------------------------------ public int evaluate (String expr) { int op1, op2, result = 0; String token; Scanner tokenizer = new Scanner (expr); while (tokenizer.hasNext()) { token = tokenizer.next(); if (isOperator(token)) { op2 = (stack.pop()).intValue(); op1 = (stack.pop()).intValue(); result = evalSingleOp (token.charAt(0), op1, op2); stack.push (result); } else stack.push (Integer.parseInt(token)); } return result; } (more…)
14.5 – PostfixEvaluator.java //------------------------------------------------------------------ // Determines if the specified token is an operator. //------------------------------------------------------------------ private boolean isOperator (String token) { return (token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/")); } //------------------------------------------------------------------ // Peforms integer evaluation on a single expression consisting of // the specified operator and operands. //------------------------------------------------------------------ private int evalSingleOp (char operation, int op1, int op2) { int result = 0; switch (operation) { case ADD: result = op1 + op2; break; case SUBTRACT: result = op1 - op2; break; case MULTIPLY: result = op1 * op2; break; case DIVIDE: result = op1 / op2; } return result; } }
Outline • Introduction to Collections • A Stack Collection • Generics • Using Stacks: Postfix Expressions • Implementing a Stack: with Arrays • References as Links • Implementing a Stack: with Links • Packages
14.7 – Implementing Stacks with Arrays • Using an array is an efficient and straightforward solution to implement a stack • Given that all activity on a stack occurs at one end, fixing the bottom of the stack at array index 0 makes the implementation relatively easy • In addition, we’ll also need an integer variable to indicate • the number of elements that are currently on the stack, and • to represent the index in the array where the next item pushed onto the stack will be stored
14.7 – Implementing Stacks with Arrays • Our array-based version of a stack will be located in a class named ArrayStack
14.8 – The ArrayStack Class //******************************************************************** // ArrayStack.java Java Foundations // // Represents an array implementation of a stack. The bottom of // the stack is kept at array index 0. //******************************************************************** package javafoundations; import javafoundations.exceptions.*; public class ArrayStack<T> implements Stack<T> { private final int DEFAULT_CAPACITY = 10; private int count; private T[] stack; //----------------------------------------------------------------- // Creates an empty stack using the default capacity. //----------------------------------------------------------------- public ArrayStack() { count = 0; stack = (T[]) (new Object[DEFAULT_CAPACITY]); } (more…)
14.8 – The ArrayStack Class //----------------------------------------------------------------- // Adds the specified element to the top of this stack, expanding // the capacity of the stack array if necessary. //----------------------------------------------------------------- public void push (T element) { if (count == stack.length) expandCapacity(); stack[count] = element; count++; } //----------------------------------------------------------------- // Returns a string representation of this stack. //----------------------------------------------------------------- public String toString() { String result = "<top of stack>\n"; for (int index=count-1; index >= 0; index--) result += stack[index] + "\n"; return result + "<bottom of stack>"; } (more…)
14.8 – The ArrayStack Class //----------------------------------------------------------------- // Creates a new array to store the contents of this stack with // twice the capacity of the old one. //----------------------------------------------------------------- private void expandCapacity() { T[] larger = (T[])(new Object[stack.length*2]); for (int index=0; index < stack.length; index++) larger[index] = stack[index]; stack = larger; } //----------------------------------------------------------------- // The following methods are left as Programming Projects. //----------------------------------------------------------------- // public T pop () throws EmptyCollectionException { } // public T peek () throws EmptyCollectionException { } // public boolean isEmpty() { } // public int size() { } }
Outline • Introduction to Collections • A Stack Collection • Generics • Using Stacks: Postfix Expressions • Implementing a Stack: with Arrays • References as Links • Implementing a Stack: with Links • Packages
14.9 – References as Links • An array is one way we can implement a linear collection • Arrays however are limited in one sense because they have a fixed size • Resizing as needed must be done carefully and is sometimes not an efficient implementation • A linked structure is a data structure that uses object reference variables to create links between objects • Linked structures are the primary alternative to an array-based implementation of a collection
14.9 – Linked Lists • A class can define as instance data an object reference to another object of the same class • Suppose we have a class named Person as follows: public class Person { private String name; private String address; private Person next; // a link to another Person object // whatever else }
14.9 – Linked Lists • Using only this one class, a linked structure can be created • One Person object contains a link to another Person object • This second object contains a reference to a third Person, etc. • This type of object is sometimes called self-referential • This kind of relationship forms the basis of a linked list • a linked structure in which one object refers to the next • creates a linear ordering on the objects in the list • Often the objects stored in a linked list are referred to generically as the nodes in the list
14.9 – Linked Lists • A linked list has no upper bound on its capacity other than the limitations of memory in the computer • It's considered to be a dynamic structure because its size grows and shrinks as needed front
14.9 – Linked Lists • A simple linked list is only one kind of linked structure • In a doubly linked list, each node in the list stores both a reference to the next element and a reference to the previous one front rear
entry 14.9 – Nonlinear Linked Structures • Linked structures need not be linear – more on that later
14.10 – Managing Linked Lists • Stacks only update one end of a list, but lists can be used for many collections • There are a few basic techniques involved in managing nodes on the list, no matter what the list is used to store • Special care must be taken when dealing with the first node in the list • Stacks only update one end of a list, but for other collections a node may be inserted or deleted anywhere in the list
14.10 – Inserting a node at the front node 2 front 1
14.10 – Inserting a node in the middle front current 2 1 node
14.10 – Deleting the first node in the list front 2 3 1 node
14.10 – Deleting an interior node front previous 1 current 2
14.11 – Elements Without Links • We still need to examine one key aspect of linked lists • We need to separate the details of the linked list structure from the elements that the list stores • The flaw in our earlier logic (see Person class) is that the self-referential Person class must be designed so that it “knows” it may become a node in a linked list • This violates the goal of separating the implementation details from the parts of the system that use the collection