Tutorial 8
Suppose:
Graph class used to model a collection of interconnected nodesGraph class because
Graph, one possible use case is traverse it using breadth-first searchIn Java:
Iterator<E> is an interface containing two methods to implement
hasNext(): returns true if there are elements left to traverse throughnext(): move the iterator to the next element and return the elementIterable<E> is an interface containing one method to implement
Since ArrayList<E> implements Iterable<E>, calling iterator() on an instance will return an iterator to the front of the instancefor-range List<Integer> myList = new ArrayList<>(List.of(1, 2, 3));
for (Integer x : myList) {
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
}
}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
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
}
}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
}
}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
}
}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
}
}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
}
}
Style of programming that allow types to be used as parameters when defining methods, classes and interfaces
List<T> is parameterised by a type parameter T
T can be substituted with any type like Integer, String, etctring into an ArrayList<Integer> will not compileList<Integer> myList = new ArrayList<>();
myList.add(42); // will work
myList.add("Hello world"); // will not compileWhat is it?
Why should you use it?
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
T and S are placeholders for any arbitrary typesBox isT and ST and S can now be referred to within the definition of the class
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
public static Integer sumStack(Stack<? extends Integer> stack);
<? extends T> and <? super T> mean?
T or subclass of T
T or superclass of T
? and E?
? cannot be used as the type of a local variable (wildcard)Benefit:
Limitation:
Suppose Jim and Bo (threads) are accessing their shared bank account at the "same" time (transaction could be microseconds apart)
A person must read the current balance before depositing / withdrawing money from the account
Jim and Bo both see $20 in the bank account
So far so good!
Bo withdraws $10 from the account
Jim saw $20 in the bank account so he rightly withdraws $15
Uh..oh but we only had $20 to begin with :(
This is a very common problem when implementing multithread code known as a race condition
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
Creational design pattern that specifies that
What is this?
Why use it?
getInstance
getInstance is called for the first timeUML Diagram
Let's refactor the code