Java Collections: Using Interfaces and Comparators for Sorting, Exams of Data Structures and Algorithms

How to use interfaces and comparators in java collections to implement stack and indexed data structures, and how to sort objects using different comparators. It covers the vector class, indexed interface, stack interface, and comparator and comparable interfaces. The document also provides examples of sorting different types of objects, such as integers, pizzas, and custom objects.

Typology: Exams

Pre 2010

Uploaded on 08/30/2009

koofers-user-n7i
koofers-user-n7i 🇺🇸

10 documents

1 / 12

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
1
CS261–DataStructures
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 changethe size of the array insertion or deletion
Homogeneity: cannot hold different types in the same array
Or can it?
Polymorphic Variables
How do you store different t ypes of data?
•Javasolution: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 implementsBag.
bag = new HashTable(); // So does HashTable.
2. Using a super-class as the type of the variable:
Animal pet = new Cat(); // Cat extends Anima l.
Object test = pet; // All class typesare o bjects.
pet = new Dog(); // Dog also extends Animal.
test = bag;
pf3
pf4
pf5
pf8
pf9
pfa

Partial preview of the text

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
    • Or can it?

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?
    • Multithreading!
  • 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)