Download Java Collections: Using Interfaces and Comparators for Sorting and more Exams Data Structures and Algorithms in PDF only on Docsity!
CS 261 – Data Structures
Vectors
Arrays
• Only core data structure in Java that is designed to hold a
collection of elements
• Fast access with an index Æ O(1)
• Price for random access:
- Difficult to change the size of the array – insertion or deletion
- Homogeneity: cannot hold different types in the same array
Polymorphic Variables
• How do you store different types of data?
• Java solution: polymorphic variables – variables that can hold
more that one type
• Two “flavors” of polymorphic variables:
1. Using an interfaces as the type of the variable:
Bag bag = new LinkedList(); // LinkedList implements Bag.
bag = new HashTable(); // So does HashTable.
2. Using a super-class as the type of the variable:
Animal pet = new Cat(); // Cat extends Animal.
Object test = pet; // All class types are objects.
pet = new Dog(); // Dog also extends Animal.
test = bag;
Using Objects
- Every class ultimately inherits from the class Object
- Thus, any Object variable can be instantiated as any class
Object val = new Pet(); // An Object variable can be an instance of Pet, val = new Rectangle(); // or of Rectangle, val = new HashTable(); // or of HashTable, or any other class.
- Primitive types (byte, short, int, …) are not objects
- Java provides “wrapper” classes Integer, Double,
Character, etc., that wrap primitives inside an Object
Vector vec = new Vector;
vec.addLast(3.14); // Error!!
vec.addLast(new Double(3.14));
double x = vec.getLast().doubleValue();
Vector Data Structure: Example
Word frequencies:
1. Read in a file (1 word per line) and place them at the end of the
vector
2. Sort them
3. Traverse and count duplicates to determine frequency
- Want to put things at the end and build the structure up
- Will be sorting, so need access to the interior
- Sound familiar?
Vector Example
- To the programmer using Vector, it’s a realization of
two ADTs – Stack and Indexed
- Stack provides an interface for placing and removing objects at
the end of the structure
- Indexed provides random access to the interior (for sorting)
- To the implementer of Vector, on the inside, we need an
underlying data structure Æ an array
- But we just said arrays have problems?
ADT Review: Stack
public interface Stack extends Collection {
public void addLast(Object elem); // Push
public Object getLast();
public void removeLast(); // Pop
- Specifies a LIFO (Last-In, First-Out) interface
Size and Capacity
- Unlike arrays, a Vector can change size
- Size is the current number of elements in the Vector
- Capacity indicates how much the vector can hold before
it must resize
Vector
Size Capacity
“Unused” elements
Vector Implementation
public class Vector implements Indexed, Stack {
// Data members.
private int count = 0;
private Object [] data = new Object[8];
// Implementation of the Collection interface.
public boolean isEmpty() { return count == 0;}
public int size() { return count;}
public Enumeration elements() {
return new IndexedEnumeration(this);
// Implementation of the Indexed interface.
// Implementation of the Stack interface.
// Vector specific methods.
More on this later
Vector: Class Specific Methods
Called “ensureCapacity” in the book:
// Additional, Vector specific (helper) methods (not defined in any interface).
public synchronized void setCapacity(int capacity) {
if (capacity <= data.length) return;
Object [] old = data;
data = new Object[capacity]; // Create new array.
// Copy elements from old array into new.
for(int i = 0; i < count; i++) data[i] = old[i];
- Time complexity:
- Best case (capacity <= data.length) Æ O(1)
- Worst/expected case (capacity > data.length) Æ O( n )
- Why is the worst case also the normal case?
Vector: Indexed Interface
// Implementation of the Indexed interface. public Object elementAt(int idx) { if (idx < 0 || idx >= count) throw new ArrayIndexOutOfBoundsException(idx); return data[idx]; }
public void setElementAt(Object val, int idx) { if (idx < 0 || idx >= count) throw new ArrayIndexOutOfBoundsException(idx);
data[idx] = val; }
- Time complexity: O(1) always
- Why check for index out of bounds when Java already
does?
Vector: Indexed Interface (cont.)
public synchronized void addElementAt(Object val, int idx){ if (idx < 0 || idx > count) throw new ArrayIndexOutOfBoundsException(idx);
setSize(count + 1); for (int i = count - 1; i > idx; i--) data[i] = data[i - 1]; data[idx] = val; }
Vector
Capacity
Size
(idx = 3) (count = 6)
Vector: Indexed Interface (cont.)
public synchronized void addElementAt(Object val, int idx){ if (idx < 0 || idx > count) throw new ArrayIndexOutOfBoundsException(idx);
setSize(count + 1); for (int i = count - 1; i > idx; i--) data[i] = data[i - 1]; data[idx] = val; }
What happens if you add so many that size > capacity?
Worst Beginning 0 O( n )
Average Middle count / 2 O( n )
Best End count O(1)
Case Position Index Time
Vector: Indexed Interface (cont.)
The setSize method as presented in the book (sort of) :
public synchronized void setSize(int size) {
while (size > data.length) // Repeatedly double capacity
setCapacity(2 * data.length);// until larger than size.
count = size;
- Time complexity:
- Best/expected case (size <= data.length) Æ O(1)
- Worst case (size > data.length) Æ setCapacity time
- What happens if setSize is called with a size parameter
many times (e.g., 40) larger than data.length?
- What is the maximum unused memory?
Vector: Indexed Interface (cont.)
A more efficient setSize method:
public synchronized void setSize(int size) {
int len = data.length;
static final int maxIncr = 1024;
while (size > len) {
if (len < maxIncr) len *= 2;
else len += maxIncr;
setCapacity(len); count = size;
- The method setCapacity is called only once
- Maximum unused memory is reduced to maxIncr – 1
Vector: Indexed Interface (cont.)
public synchronized void removeElementAt(int idx) {
if (idx < 0 || idx >= count)
throw new NoSuchElementException();
count--;
for(int i = idx; i < count; i++)
data[i] = data[i + 1];
data[count] = null;
Worst Beginning 0 O( n )
Average Middle count / 2 O( n )
Best End count - 1 O(1)
Case Position Index Time
Synchronized
- No two synchronized methods from the same object are
ever allowed to be executing at the same time
- How could this happen?
- Why is this a problem?
- Simultaneously changing the data structure in two (or more)
threads Æ undefined results
- E.g.: setSize and addElement
Vector: Stack Interface
// Implementation of the Stack interface.
public void addLast(Object val) {
addElementAt(val, count);
public Object getLast() {
return data[count – 1];
public void removeLast() {
removeElementAt(count – 1);
Time complexity:
- addLast: Best/expected case Æ O(1)
Worst case Æ O( n )
- getLast and removeLast: O(1) always
Comparisons
Two approaches in the Java standard library:
1. Comparator interface:
public interface Comparator extends Serializable { public int compare(Object left, Object right); } // Compare two objects (that do not necessarily implement Comparable).
2. Comparable interface:
public interface Comparable extends Serializable { public int compareTo(Object val); } // Compare this object with another.
If object’s class implements Comparable, can use:
public class DefaultComparator implements Comparator { public int compare(Object left, Object right) { return ((Comparable)left).compareTo(right); } } // Compare two objects (that do implement Comparable).
Generic sorts
need this.
Comparator Objects
What if my objects are Integers (the wrapper class for int) , how can I
compare them for sorting?
public class IntegerCompare implements Comparator { public int compare(Object left, Object rght) { int iLeft = ((Integer)left).intValue(); int iRght = ((Integer)rght).intValue(); if (iLeft < iRght) return –1; if (iLeft == iRght) return 0; return 1; } }
However, since the Integer class implements Comparable:
public class DefaultComparator implements Comparator { public int compare(Object left, Object rght) { Comparable test = (Comparable)left; // Assumes that objects return test.compareTo(rght); // implement Comparable. } }
Comparator Objects (cont.)
If objects don’t implement Comparable, need to use a
Comparator class:
public class Pizza { public int size, // Can compare pizzas to each other based toppings; // on any of these attributes. public double price, quality; ... }
If we want to compare based on size, we write the class:
public class PizzaSizeCompare implements Comparator { public int compare(Object left, Object rght) { int size1 = ((Pizza)left).size; int size2 = ((Pizza)rght).size; if (size1 < size2) return –1; if (size1 == size2) return 0; return 1; } }
Comparator Objects: Sorting Pizza’s by Size
Collection
of Pizzas
(implements
Indexed) Sorting Algorithm
(implements
SortAlgorithm)
PizzaSizeComparator
(implements Comparator)
Comparator Objects: Sorting Pizza’s by Price
Collection
of Pizzas
(implements
Indexed) Sorting
Algorithm
(implements
SortAlgorithm)
PizzaPriceComparator
(implements Comparator)
Comparisons (cont.)
So, three options for comparing objects for sorting:
1. Supply a class that implements Comparator (most general) :
public class PizzaPriceCompare implements Comparator { public int compare(Object left, Object rght) { int price1 = ((Pizza)left).price; int price2 = ((Pizza)rght).price; return price1 < price2? –1 : (price1 > price2? 1 : 0); } } // Compare two pizzas according to the number of their toppings.
(Could also create Comparator classes to sort pizzas by size, price, etc.)
2. Have object class implement Comparable and then use
DefaultComparator (limits sorting to a single property)
3. Have object class implement Comparable and then use the
compareTo function directly (requires that object class implements
Comparable)