Lecture 07 Complex Data Structures
Joseph Haugh
University of New Mexico
Free Recall
- Get out a sheet of paper or open a text editor
- For 2 minutes write down whatever comes to mind about the last class
- This could be topics you learned
- Questions you had
- Connections you made
What is a Data Structure?
- A data structure is defined by:
- an organization of the data being stored
- a set of operations for effective access to this data
- Algorithmic complexity (efficiency) is directly tied to data organization
- searching for an element in an unsorted array takes linear time: 232 = 4, 294, 967, 296
- searching for an element in a sorted array takes logarithmic time: 32
- Key to computing efficiency is reducing algorithmic complexity
Relation to Abstract Data Types
- Abstract Data Type specifications are given in terms of:
- an abstract data representation
- a set of operations over the abstract representation
- signature or interface
- semantics
- A data structure is a concrete realization of the ADT
- it should preserve encapsulation
- it can be analyzed with respect to performance
- at the formal level - time and space complexity
- at the execution level - runtime and resource usage
Common Data Structures
- Some basic data structures are built into the language:
- Some data structures are provided in standard libraries:
- linked lists
- hash tables
- search trees
- Other data structures need to be explicitly coded:
- Generic types facilitate generality and reuse
- Java collections expand the range of ready to use common data structures
- designed, coded, and optimized
Illustration: Linked List
![]()
Illustration: Defining a linked list in Java
public class LinkedList {
private Node first;
private Node last;
public LinkedList() {
last = first = null;
}
public void add(Object element) {
// add element to the end
}
public void remove() {
// remove element from the front
}
public Object getHead() {
// return the head of the list
}
public LinkedList getTail() {
// return the tail of the list
}
}
Illustration: Defining a Node
public class Node {
private Node next;
private Object element;
public Node(Object element, Node next) {
this.element = element;
this.next = next;
}
public Object getElement() { return element; }
public void setElement() {
this.element = element;
}
public Node getNext() { return next; }
public void setNext(Node next) {
this.next = next;
}
}
Generics to the rescue!
public class LinkedList<T> {
private Node<T> first;
private Node<T> last;
//...
public class Node<T> {
private Node<T> next;
private T element;
//...
}
Design Concerns: Memory Leaks
- Memory leaks are serious programming errors-hard to debug
- They happen when references are maintained to objects no longer in use
- The garbage collector cannot reclaim the space
Effective Java: Item 7 Eliminate Obsolete Object References
- Memory leaks can be difficult to spot
- Code with a memory leak can appear to work fine for years
- Might only cause problems in extreme cases
- It is worth knowing patterns to avoid
- Download the code here
Design Concerns: Entanglement
- Assignment statements such as
x = y result in both x and y referring to the same object
- changes carried out by invoking a method
x.set(v)
- alter the object
- are visible to y when invoking
y.get()
- Often this is not the desired outcome
Entanglement Example
// Simple Mutable object
public class Mutable {
private int x;
public Mutable(int x) {
this.x = x;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
}
void main() {
Mutable m1 = new Mutable(1);
Mutable m2 = m1;
IO.println("m1.getX() = " + m1.getX()); // 1
IO.println("m2.getX() = " + m2.getX()); // 1
m2.setX(2);
IO.println("m1.getX() = " + m1.getX()); // 2
IO.println("m2.getX() = " + m2.getX()); // 2
}
Object Cloning
- Cloning is a powerful, but controversial, feature in Java
- Java provides two forms of cloning
- shallow – a new object is created and the fields of the original are copied without change
- Default strategy provided by
Object.clone()
- deep – a new object is created and cloning is applied recursively to each field
- Must override
clone() to implement this strategy
- Great care needs to be exercised to apply it correctly
- It is generally recommended to provide a copy constructor
Cloning vs Copy Constructor
- Copy constructor is easier as we do not need to handle
CloneNotSupportedException
clone method returns Object which we then need to cast
- We cannot assign to final fields in clone method
Cloning vs Copy Constructor
public class BadClone
implements Cloneable {
private final int x;
public BadClone(int x) {
this.x = x;
}
// Omits throws for brevity
@Override
protected Object clone() {
BadClone badClone =
(BadClone) super.clone();
// Can't change x!
// badClone.x = 5;
return badClone;
}
}
public class GoodClone {
private final int x;
public GoodClone(int x) {
this.x = x;
}
// Copy constructor
public GoodClone(GoodClone other) {
// I can now set x!
this.x = other.x + 1;
}
}
Design Concerns: Encapsulation Cracks
- The object creator retaining access:
- exposes access to the data structure internals
- introduces side effects that break the class contract
- cloning/copying is one way to avoid this design flaw
Illustration: Creator Retaining Access
![]()
Design Concerns: Encapsulation Cracks
- An internal object is returned to the caller
- providing access to the data structure internals
- enabling side effects that break the class contract
- creating the potential to break the integrity of the data structure
Illustration: Returning Internal Object
![]()
Collections
- Collections or containers:
- allow programmers to hold and organize sets of objects
- in useful and efficient ways
- as part of consistent and flexible framework
- Java provides us with many standard collection interfaces and implementations
Core Collection Interfaces
- Collection – root of the collection hierarchy
- Set – no duplicates
- List – ordered collection, sequence
- Queue – holds elements for processing
- Deque – double ended queue
- Map – maps keys to values
Collection Interface
- size, isEmpty
- contains
- add, addAll
- remove, removeAll, retainAll, clear
- toArray
- iterator (because Collection implements Iterable)
- Optional methods that are not supported by a specific implementation throw UnsupportedOperationException
List
- add, addAll – add to end of the list
- remove – removes first occurrence
- iterator, listIterator
- indexOf, lastIndexOf – find index of element
- get, set – access element at given index
- subList – view portion of list as a List
Queue
- Insert – add, offer
- Remove – remove, poll
- Examine – element, peek
- Queues usually use FIFO order.
- PriorityQueue will use natural ordering or a Comparator.
Map
- Maps keys to values. Does not implement Collection itself, but has three collection views
- keySet – Set of the keys
- values – Collection of values
- entrySet – Set of key-value mappings.
- Beware of using mutable objects as keys!
- put – associate a key with a value
- get – get value associated with key
- remove – remove key/value mapping
- containsKey, containsValue
Bad Map
- What would be so bad about having mutable keys?
- Well once you change an element in the key which contributes to say the hashing function it won’t work in the map anymore!
- Example code
Iterator and Iterable
- Iterator has three methods
- hasNext – Are there more elements?
- next – Return the next element
- remove – Remove the last element returned (optional)
- Iterable only has one method
- iterator – returns an Iterator
- Implementing this interface allows object to be target of for-each
Iterator
- The ability to examine an entire collection of objects is a helpful feature
- Java provides the Iterable interface specifically for this purpose
- A specific iterator may or may not take a snapshot first
- If not, use of other methods may affect the results
Iterator: Protecting Encapsulation
- The iterator:
- is made available outside the collection
- does not reveal anything about the internal organization
- has access to the internal organization of the data
Iterator: Protecting Encapsulation
![]()
Data Structure Design
- Research has led to the development of an arsenal of specialized data structures
- Search and use them when developing a new program
- Don’t reinvent the wheel