COMP2511

Tutorial 8

Today

  • Reminders
  • Iterator pattern
  • Generic Programming
  • Concurrency 
  • Singleton Pattern

Reminders

  • Assignment 2 is due on Friday Week 9 at 5pm
  • No lab marking in week 10
    • Please ensure all your labs are marked by week 9
  • Sample exam with the sample environment during week 10 lab
    • Highly recommend familiarising with the exam layout and environment
    • Tutorial 10 is still on

Iterator Pattern

Iterator Pattern

Suppose:

  • We have been given a Graph class used to model a collection of interconnected nodes
  • We can't access the implementation of the Graph class because 
    • It will break encapsulation
  • Given an instance of the Graph, one possible use case is traverse it using breadth-first search
  • Iterator pattern allow clients to traverse the graph in a way without exposing the underlying implementation

Iterator Pattern

In Java:

  • Iterator<E> is an interface containing two methods to implement
    • hasNext(): returns true if there are elements left to traverse through
    • next(): move the iterator to the next element and return the element
    • Akin to a "pointer" sitting somewhere in the collection
  • Iterable<E> is an interface containing one method to implement
    • iterator(): returns an iterator of type Iterator<E>
      • Since ArrayList<E> implements Iterable<E>, calling iterator() on an instance will return an iterator to the front of the instance
    • Classes that implement the interface can be iterated through using for-range 
List<Integer> myList = new ArrayList<>(List.of(1, 2, 3));
for (Integer x : myList) {
	System.out.println(x);
}

Iterator Pattern

public class Graph<T> implements Iterable<T> {
	... hidden data structures for the graph ...
	... attributes and methods ...
    
    public Iterator<T> iterator() {
    	returns iterator to "before" the 
        source node of the graph
    }
}
public static void main(String[] args) {
	Graph<Integer> g = new Graph<>();
   	... fill g with vertices and edges ...
    
    Iterator<Integer> it = g.iterator();
    Integer src = it.next();
   	
    while (it.hasNext()) {
    	System.out.println(it.next());
    }
    
    for (Integer x : g) {
    	System.out.println(x);
    }
}

g

Iterator Pattern

public static void main(String[] args) {
	Graph<Integer> g = new Graph<>();
   	... fill g with vertices and edges ...
    
    Iterator<Integer> it = g.iterator();
    Integer src = it.next();
   	
    while (it.hasNext()) {
    	System.out.println(it.next());
    }
    
    for (Integer x : g) {
    	System.out.println(x);
    }
}
public class Graph<T> implements Iterable<T> {
	... hidden data structures for the graph ...
	... attributes and methods ...
    
    public Iterator<T> iterator() {
    	returns iterator to "before" the 
        source node of the graph
    }
}

Iterator Pattern

public static void main(String[] args) {
	Graph<Integer> g = new Graph<>();
   	... fill g with vertices and edges ...
    
    Iterator<Integer> it = g.iterator();
    Integer src = it.next();
   	
    while (it.hasNext()) {
    	System.out.println(it.next());
    }
    
    for (Integer x : g) {
    	System.out.println(x);
    }
}
public class Graph<T> implements Iterable<T> {
	... hidden data structures for the graph ...
	... attributes and methods ...
    
    public Iterator<T> iterator() {
    	returns iterator to "before" the 
        source node of the graph
    }
}

Iterator Pattern

public static void main(String[] args) {
	Graph<Integer> g = new Graph<>();
   	... fill g with vertices and edges ...
    
    Iterator<Integer> it = g.iterator();
    Integer src = it.next();
   	
    while (it.hasNext()) {
    	System.out.println(it.next());
    }
    
    for (Integer x : g) {
    	System.out.println(x);
    }
}
public class Graph<T> implements Iterable<T> {
	... hidden data structures for the graph ...
	... attributes and methods ...
    
    public Iterator<T> iterator() {
    	returns iterator to "before" the 
        source node of the graph
    }
}

Iterator Pattern

public static void main(String[] args) {
	Graph<Integer> g = new Graph<>();
   	... fill g with vertices and edges ...
    
    Iterator<Integer> it = g.iterator();
    Integer src = it.next();
   	
    while (it.hasNext()) {
    	System.out.println(it.next());
    }
    
    for (Integer x : g) {
    	System.out.println(x);
    }
}
public class Graph<T> implements Iterable<T> {
	... hidden data structures for the graph ...
	... attributes and methods ...
    
    public Iterator<T> iterator() {
    	returns iterator to "before" the 
        source node of the graph
    }
}

Iterator Pattern

public static void main(String[] args) {
	Graph<Integer> g = new Graph<>();
   	... fill g with vertices and edges ...
    
    Iterator<Integer> it = g.iterator();
    Integer src = it.next();
   	
    while (it.hasNext()) {
    	System.out.println(it.next());
    }
    
    for (Integer x : g) {
    	System.out.println(x);
    }
}
public class Graph<T> implements Iterable<T> {
	... hidden data structures for the graph ...
	... attributes and methods ...
    
    public Iterator<T> iterator() {
    	returns iterator to "before" the 
        source node of the graph
    }
}

Generic Programming

Generic Programming

 

Style of programming that allow types to be used as parameters when defining methods, classes and interfaces

 

  • Implement code once and it will work for a collection of different types
    • List<T> is parameterised by a type parameter T
    • This means T can be substituted with any type like Integer, String, etc
  • Offer strong type checking at compile time
    • Adding a String into an ArrayList<Integer> will not compile
List<Integer> myList = new ArrayList<>();
myList.add(42); // will work
myList.add("Hello world"); // will not compile

What is it?

Why should you use it?

Generic Programming

public class Box<T, S> {
    
    private T value1; 
    private S value2; 
    
    public Box(T v1, S v2) {
        this.value1 = value1;
        this.value2 = value2;
    }
    
    public void setValue(T v1, S v2) {
        this.value1 = v2;
        this.value2 = v2;
    }
    
    public T getValue1() {
        return value1;
    }
    
    public S getValue2() {
        return value2;
    }
}

Basic Syntax

  • The generic type parameters T and S are placeholders for any arbitrary types
  • Each instance of a Box is
    parameterised by 2 arbitrary types T and S
  • T and S can now be referred to within the definition of the class
    • Type of member variables
    • Type of arguments for methods
    • Return type of methods

Generic Programming

Inside src/stack, there are a series of stubs for a Stack class which takes in a generic type. There are a series of tests inside StackTest.java which currently fail

 

Implement the methods so that the tests pass, using an ArrayList to store the internal data structure

Generic Programming

public static Integer sumStack(Stack<? extends Integer> stack);
  • What does <? extends T> and <? super T> mean?
    • Extends: parameterised type must be T or subclass of T
    • Super: parameterised type must be T or superclass of T
  • What is the difference between ? and E?
    • ? cannot be used as the type of a local variable (wildcard)

Concurrency

Concurrency

Threads

  • Sequence of instructions that the CPU can choose to execute at any given time (unpredictable)
  • Different threads can be scheduled very closely to each other to give the illusion that they are running at the same time

Concurrency

Threads

Benefit:

  • Generally improves the performance of a program
  • Restaurants with only one employee acting as both the chef and the waiter will be less efficient than restaurants with dedicated chefs and waiters

Limitation:

  • Multithreaded code is generally more difficult to reason about and implement correctly

Concurrency

Suppose Jim and Bo (threads) are accessing their shared bank account at the "same" time (transaction could be microseconds apart)

Concurrency

A person must read the current balance before depositing / withdrawing money from the account

Jim and Bo both see $20 in the bank account

Concurrency

So far so good!

Bo withdraws $10 from the account

Concurrency

Jim saw $20 in the bank account so he rightly withdraws $15

Uh..oh but we only had $20 to begin with :(

Concurrency

This is a very common problem when implementing multithread code known as a race condition

  • Occurs when two or more threads attempt to read and write to a shared value at the "same" time
  • Outcome is non-deterministic and dependent on the order in which threads are scheduled on the CPU

Concurrency

Solution: synchronise code that may read and write to a shared value

Idea is explored in more depth in COMP3231

Enforce that a read + (withdraw / deposit) must be completed before the next can begin

Singleton Pattern

Singleton Pattern

 

Creational design pattern that specifies that

  • A class can only have one active instance
  • Accessing the instance must be through a global access point

 

  • Control access to some shared resource
    • i.e. bank, database connection, etc
  • Avoid initialisation overhead when only 1 copy of an instance is required

What is this?

Why use it?

Singleton Pattern

  • The class holds a static instance of itself via composition
  • All constructors are private to prevent arbitrary instances from being created
  • Accessing the instance must be through the static method getInstance
  • Class is only instantiated once when the getInstance is called for the first time

UML Diagram

Singleton Pattern

Let's refactor the code

  • Change the system to use the singleton pattern
  • Incorporate the Java synchronisation primitive to eliminate data races

The End

COMP2511 Tutorial 8

By matthewliu-2

COMP2511 Tutorial 8

  • 94