Chapter 4: Array Implementations of Collection ADTs
Abstract Data Types (ADTs) - Creation Process
Definition
- An ADT is a data type with characteristics and operations specified without implementation details
- At this point, the ADT is abstract - focus on what operations do, not how they do them
Causes
- Not specified in notes
Goals / Objectives
- Create a specification that describes characteristics and operations without implementation or usage details
- Separate the interface (what operations do) from implementation (how they do it)
Importance
- Not specified in notes
Procedures
Step 1: Write the ADT specification
- Describe characteristics of the data type
- Define the set of operations for manipulating the data
- Should not include any implementation or usage details
Step 2: Implement the ADT
- a. Write a Java interface
- Include all operations from the ADT specification
- All methods are abstract methods
- b. Write a Java class
- Implements the Java interface from step 2a
- Determine how to represent the data
- Implement all operations from the interface
- Operations include core operations (from specification) and utility operations (private methods to support core operations)
Step 3: Use the ADT in a client program or application
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- Not specified in notes
Examples
- Not specified in notes
Design Issues for ADT Operations
Definition
- Special or unusual conditions that must be considered when specifying ADT operations
Causes
- Operations may not work properly when given invalid positions
- Operations may not be meaningful on empty collections
- Collections could become full
Goals / Objectives
- Decide how to handle special/unusual conditions in ADT specifications
- Specify how operations handle error situations
Importance
- ADT specifications should include details on how special conditions are handled by various operations
Procedures
- Not specified in notes
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- Not specified in notes
Examples
For Lists:
- add, remove, replace, getEntry work OK when valid position given
- remove, replace, and getEntry not meaningful on empty lists
- A list could become full, affecting add operation
Possible Solutions:
- Assume invalid situations will not occur
- Ignore invalid situations
- Make reasonable assumptions, act in predictable way
- Return boolean value indicating success or failure
- Throw an exception
Collection ADTs
Definition
- An ADT that can store a collection of objects
- A linear collection stores its entries in a linear sequence
Causes
- Not specified in notes
Goals / Objectives
- Not specified in notes
Importance
- Not specified in notes
Procedures
- Not specified in notes
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- Collections differ in restrictions they place on how entries may be added, removed, or accessed
Examples
- List
- Stack
- Queue
Lists - General Concept
Definition
- A list provides a way to organize data
- Items are organized in a sequence
Causes
- Not specified in notes
Goals / Objectives
- Not specified in notes
Importance
- Not specified in notes
Procedures
Operations on Lists:
- Add new entry (at end or anywhere)
- Remove an item
- Replace an entry
- Get an entry at a specified position
- Count how many entries
- Check if list is empty or full
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- Not specified in notes
Examples
- To-do list with tasks like “Read Chapter 4”, “Call home”, “Buy card for Sue”
Array Implementation of Lists
Definition
- Implementation where entries in the list have positions beginning with 1 (consistent with typical everyday lists)
- Uses an array to store list elements
Causes
- Not specified in notes
Goals / Objectives
- Implement all list operations using array-based storage
Importance
- Not specified in notes
Procedures
Data Fields:
T[] array- array of list entriesint numberOfEntries- current number of entries in the list
Method add(newEntry):
- Assign new value at end of array
- Increment numberOfEntries
Method add(newPosition, newEntry):
- Use utility method makeRoom() to shift elements ahead
- Add new entry at specified position
Method remove(givenPosition):
- Assign entry at array location to temporary variable (to be returned)
- Shift entries from location to avoid gap using removeGap()
- Exception: when removing last entry
- Must handle error situations:
- Invalid position
- Empty list
- Invalid call returns null value
Method replace, getEntry, contains:
- Algorithms not detailed in notes but mentioned as exercises
Advantages & Disadvantages
Advantages:
- Retrieving an entry is fast
- Adding an entry at the end of the list is fast
Disadvantages:
- Adding or removing an entry between other entries requires shifting elements in the array
- Increasing the size of the array requires copying elements
Impact / Effect
- Not specified in notes
Examples
- Reference to Chapter4\adt\ListInterface.java and ArrayList.java
- Registration.java (client application)
Generic Types
Definition
- Used in Java interfaces and classes that implement collection ADTs to specify and constrain the type of objects being stored in the collection
- The interface/class name is followed by an identifier enclosed in angle brackets (e.g.,
ListInterface<T>) - T represents the data type within the class definition and can be any identifier (usually a single capital letter)
Causes
- Not specified in notes
Goals / Objectives
- Specify and constrain the type of objects in a collection
- Enable type safety without writing separate classes for each data type
Importance
- Not specified in notes
Procedures
Usage:
- When using the class, supply an actual type argument to replace T
- Example:
ListInterface<String> taskList; - Wherever T appears in the definition, the supplied type (String) will be used
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- A generic type must be a reference type, not a primitive type
Examples
public interface ListInterface<T>ListInterface<String> taskList;- now T is replaced with String throughout
Array Expansion (Dynamic Expansion)
Definition
- The process of moving an array’s contents to a larger array when it becomes full
- Also called dynamic expansion
Causes
- An array has a fixed size
- When array becomes full, need for larger list arises
Goals / Objectives
- Allow the array-based collection to grow beyond its initial capacity
- Avoid “full array” limitations
Importance
- Without expansion, array-based collections would have hard size limits
Procedures
- Copy data from original array to new, larger location
- Manipulate references so new location keeps name of original array
Utility Methods Needed:
isArrayFull()- to determine if array is already fulldoubleArray()- to expand the array
Implementation:
int[] myArray = new int[INITIAL_SIZE];
int[] oldArray = myArray; // save reference
myArray = new int[2 * oldArray.length]; // double size
for (int index = 0; index < oldArray.length; index++)
myArray[index] = oldArray[index];
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- The array can continue to grow as needed
- Requires copying overhead
Examples
- Figures 5-6 and 5-7 show the expansion process visually
Array of Object References
Definition
- An array of objects actually contains references to those objects, not the objects themselves
- For simplicity, figures often portray arrays as if they actually contained objects
Causes
- Not specified in notes
Goals / Objectives
- Not specified in notes
Importance
- Understanding this distinction is important for proper memory management and understanding of data structures
Procedures
- Not specified in notes
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- Not specified in notes
Examples
- Diagrams show array elements pointing to objects (Alice, Bob, Doug, Haley) versus simplified view showing objects directly in array
Stacks - General Concept
Definition
- A collection where new items are added to the top
- The item most recently added is always on the top
- The most recently added item is always the next item to be removed
- Exhibits LIFO (Last In, First Out) behavior
Causes
- Not specified in notes
Goals / Objectives
- Not specified in notes
Importance
- Not specified in notes
Procedures
Basic Operations:
push(newEntry)- add to toppop()- remove from toppeek()- view top without removingisEmpty()- check if emptyclear()- remove all entries
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- Not specified in notes
Examples
- Stack of books
- Stack of plates
Array Implementation of Stacks
Definition
- Implementation using an array where:
- Array’s first element represents the bottom of the stack
- Last occupied location represents the stack’s top
- This approach avoids shifting elements
Causes
- Not specified in notes
Goals / Objectives
- Implement stack operations efficiently using array-based storage
Importance
- Avoiding element shifts improves performance
Procedures
Data Fields:
T[] array- array of stack entriesint topIndex- index of top entry (initialized to -1 for empty stack)
Method push(newEntry):
- Increment topIndex
- Assign new value at array location indicated by topIndex
Method pop():
- Assign entry at array location topIndex to temporary variable (to be returned)
- Optionally set array[topIndex] to null (for garbage collection)
- Decrement topIndex
Method isEmpty():
- Stack is empty if topIndex equals -1
Array Expansion:
- Change isFull() to always return false
- The add() methods will double the size of array when it becomes full
- Declare private method isArrayFull() called by add() methods
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- Not specified in notes
Examples
- Reference to Chapter4\adt\StackInterface.java and ArrayStack.java
- StringReversal.java (client application)
- Exercise 4.1: convertNumberToBinary(int number) - converts number to binary using a stack
Queues - General Concept
Definition
- A queue organizes entries according to order of entry
- Exhibits FIFO (First In, First Out) behavior
- All additions are at the back of the queue
- Front of queue has items added first
Causes
- Not specified in notes
Goals / Objectives
- Not specified in notes
Importance
- Not specified in notes
Procedures
Basic Operations:
enqueue(newEntry)- add to backdequeue()- remove from frontgetFront()- view front without removingisEmpty()- check if emptyclear()- remove all entries
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- Not specified in notes
Examples
- People waiting in line for tickets
- Train cars
- Ducks in a row
- Cars at car wash
Array Implementation of Queues - Method 1: Linear Array with Fixed Front
Definition
- The front of the queue is fixed to queue[0]
- frontIndex is always 0
- backIndex initialized to -1 to indicate an empty queue
Causes
- Not specified in notes
Goals / Objectives
- Implement queue using straightforward approach similar to how physical queues work
Importance
- Easy to understand as similar to how people move forward in real queues
Procedures
Data Fields:
T[] array- array of queue entriesint frontIndex- always 0 (fixed)int backIndex- index of back entry
Method enqueue(newEntry):
- Increment backIndex
- Assign new value at array location indicated by backIndex
Method dequeue():
- Assign entry at array location 0 to temporary variable (to be returned)
- Shift entries from array location 1 to backIndex one step towards front
- Decrement backIndex
Method isEmpty():
- Queue is empty if backIndex equals -1
Advantages & Disadvantages
Advantages:
- Easy to understand as similar to how everyone in queue moves forward a step
Disadvantages:
- The dequeue operation is inefficient - overhead incurred as must shift entries each time an entry is removed
Impact / Effect
- Not specified in notes
Examples
- Not specified in notes
Array Implementation of Queues - Method 2: Linear Array with Dynamic Front
Definition
- frontIndex is dynamic (not fixed) - it updates/moves instead of shifting elements
- backIndex initialized to -1; frontIndex initialized to 0
Causes
- To avoid the inefficiency of shifting entries after each dequeue operation in Method 1
Goals / Objectives
- Eliminate the overhead of shifting elements on each dequeue
Importance
- Improves efficiency compared to Method 1
Procedures
Data Fields:
T[] array- array of queue entriesint frontIndex- index of front entry (dynamic)int backIndex- index of back entry
Method enqueue(newEntry):
- Increment backIndex
- Assign new value at array location indicated by backIndex
Method dequeue():
- Assign entry at array location frontIndex to temporary variable (to be returned)
- Increment frontIndex (instead of shifting elements)
Method isEmpty():
- Queue is empty if backIndex < frontIndex
Advantages & Disadvantages
Advantages:
- Do not have to shift entries after each dequeue operation
Disadvantages:
- Rightward drift problem - the array can become “full” when the last array location has been occupied but there are empty locations in the beginning part of the array
- Question: How to use the empty locations?
Impact / Effect
- Leads to wasted array space at the beginning
- Motivates the need for circular array approach (Method 3)
Examples
- Figure 24-6 shows the array after initially filling and after removing front twice
Array Implementation of Queues - Method 3: Circular Array
Definition
- When queue reaches end of array, add subsequent entries to beginning
- Array behaves as though it were circular - first location follows last one
- backIndex initialized to -1; frontIndex initialized to 0
- Uses modulo arithmetic to update indices
Causes
- To solve the rightward drift problem of Method 2
- To utilize empty locations at the beginning of the array
Goals / Objectives
- Eliminate wasted array locations
- Avoid shifting entries after last array location is used
- Allow the array to “wrap around”
Importance
- Solves both major problems: shifting overhead and wasted space
Procedures
Data Fields:
T[] array- array of queue entriesint frontIndex- index of front entryint backIndex- index of back entry
Method enqueue(newEntry):
- Update backIndex using modulo arithmetic:
backIndex = (backIndex + 1) % array.length - Assign new value at array location indicated by backIndex
Method dequeue():
- Assign entry at array location frontIndex to temporary variable (to be returned)
- Update frontIndex using modulo arithmetic:
frontIndex = (frontIndex + 1) % array.length
Detecting Empty and Full Queues - Problem:
- With circular array,
frontIndex == backIndex + 1both when queue is empty AND when full
Solution 1: Use a counter
- Keep track of total entries in queue
- Empty queue detected when counter is 0
- Full queue detected when counter equals array length
Solution 2: Leave one unused location
- Empty queue detected when frontIndex is one location “in front” of backIndex (with wraparound)
- Full queue detected when only one vacant array location left
- Queue is empty if:
(backIndex + 1) % array.length == frontIndex
Method doubleArray() for Circular Array:
- Must carefully copy elements maintaining queue order
- Front elements copied to beginning of new array
- Adjust indices appropriately
Advantages & Disadvantages
Advantages:
- No rightward drift problem
- No wasted array locations
- Do not have to shift entries after last array location is used
Disadvantages:
- More complex logic for detecting empty vs full queue
- Requires modulo arithmetic
Impact / Effect
- Most efficient array-based queue implementation
Examples
- Figure 24-7 shows circular array in various states (full, after removals, wrapping around)
- Figure 24-8 shows seven-location circular array with one unused location
- Figure 24-9 shows array-based queue operations
- Figure 24-10 shows doubling the size of array-based queue
- Reference to Chapter4\adt\QueueInterface.java, ArrayQueue.java, CircularArrayQueue.java
- SharePortfolio.java and SharePortfolioDriver.java (client applications)
Iterators
Definition
- An object that enables you to traverse a collection of data, beginning with the first entry
- Acts like a cursor or pointer, moving about on a data structure and locating individual elements for access
- A software design pattern that abstracts the process of scanning through a collection of elements one element at a time
Causes
- Need to go through entries of a collection in order, one at a time
Goals / Objectives
- Provide a uniform way for traversing elements in various types of collections
- Enable modification of the list while traversing it
Importance
- Iteration is such a common operation that Java provides interfaces for it
- Provides consistent interface across different collection types
Procedures
During one complete iteration:
- Each entry is considered once
Iterator may be manipulated to:
- Check whether next entry exists
- Advance to next entry
- Get a reference to current entry
- Modify the list as you traverse it
Java’s Iterator Interface (java.util.Iterator):
- Specifies a generic type for entries
hasNext()- Checks if next entry existsnext()- Returns next entry and advances iterator to next entry (throws NoSuchElementException if no more elements)remove()- Removes entry that was returned by last call to next()
Implementation as Inner Class:
- Define iterator class as private inner class of the collection ADT
- Has direct access to ADT’s data fields
- Provide public method getIterator() that returns an iterator to the collection
- Includes call to inner class iterator’s constructor
- Enables client to create an iterator
Advantages & Disadvantages
- Not specified in notes
Impact / Effect
- Not specified in notes
Examples
ArrayQueueIterator (inner class):
private class ArrayQueueIterator implements Iterator<T> {
private int nextIndex = 0;
public boolean hasNext() {
return nextIndex <= backIndex;
}
public T next() {
if (hasNext()) {
T nextEntry = array[nextIndex];
nextIndex++;
return nextEntry;
} else {
return null;
}
}
}
Public method in ArrayQueue:
public Iterator<T> getIterator() {
return new ArrayQueueIterator();
}
Sample implementations:
- Chapter4\adt\QueueInterface.java (with getIterator() method)
- Chapter4\adt\ArrayQueue.java
- Chapter4\adt\SharePortfolio.java (with countTotalUnitShares() and getSharePortfolioCapital())
- Chapter4\client\SharePortfolioDriver.java