CISC 3130
Data Structures
Building Containers

Overview

There are only two ways of introducing a new type into Java: having an array with an element type, or creating a class.

Relationships Among Classes

Implementation Approaches / Relationships Among Classes

Independent

Composition

Inheritance

Interface Inheritance

Summary

An Example: A Counter Class

A Basic Counter

public class counter {
	void up() {val++;}	
	void down() {val--;}	

	int val() {return val;}

	private int val = 0;
}

Independent

public class UpperBoundedCounter {
	UpperBoundedCounter(int limit) {this.limit = limit;} 

	void up() {if (val < limit) val++;}	
	void down() {val--;}	

	int val() {return val;}

	private int val = 0;
	private int limit;
}

Composition

public class UpperBoundedCounter {
	UpperBoundedCounter(int limit) {this.limit = limit;} 

	// New method
	void up() {if (counter.val() < limit) counter.up();}	

	//Delegation methods
	void down() {counter.down();}
	int val() {return counter.val();}

	// Contained/member type
	private Counter counter = new Counter();
	private int limit;
}

Inheritance

public class UpperBoundedCounter extends Counter {
	UpperBoundedCounter(int limit) {this.limit = limit;} 

	// Overridden methods
	void up() {if (val() < limit) super.up();}	

	private int limit;
}

Inheritance of Interfaces

We'll illustrate this with a hierarchy of shapes
interface Shape {
	int getArea();
}
interface Ovalesque extends Shape {
	getArcLength();
}
interface Quadrilateral extends Shape {
	getDiagonal();
}
class Circle implements Ovalesque {
	double getArea() {…}
	double getArcLength() {…}
}
class Rectangle implements  Quadrilateral {
	int getArea() {…}
	double getDiagonal() {…}
}

The Above In the Context of Containers

We'll be getting into this in much more detail in the next several lectures; i.e., actually examining the construction of the classic containers covered in Lecture 5 using each of the above techniques. For the moment, we'll just present some simple examples.

What we did in Lecture / Lab 5 is different than any of these three approaches. What we did there was use an existing class as a stack, or queue, or deque, etc, as opposed to creating a stack, queue, deque, etc using some other class. I.e., we used an ArrayList as a stack, or a LinkedList as a deque. WHat we are doing here (an din general when we want a container) is creating a new class (e.g., Stack, Queue, Deque) using another container via composition or inheritance or an altogether independent implementation.

Composition

For this technique, we will implement a Stack class using ArrayList as the underlying container.
class ArrayStack<E> {
	…
	void push(E e) {arrayList.add(e);}		// append
	E pop() {return arrayList.remove(arrayList.size()-1);} // remove last element
	boolean empty() {return arrayList.isEmpty();}
	…
	ArrayList<E> arrayList;
}

Advantages

Disadvantages

(Class) Inheritance

We will implement a (Fifo) Queue using a ArrayDeque
class FifoQueue<E> extends ArrayDeque<E> {}

Advantages

Disadvantages

(Interface) Inheritance

Set and SortedSet, as illustrated, above are an example.

Advantages

Disadvantages

Independent

We'll implement a Stack using a built-in array.
class Stack<E> {
	Stack() {…}
	void push(E e) {arr[++tos] = e;}
	E pop(E e) {
		if (empty()) throw … 
		return arr[tos--];
	}
	void empty() {return tos == -1;}

	E [] arr;
	int tos = -1;
}

Advantages

Disadvantages

Overloading Functions for Specialized Functionality

Code Used in the Lecture