COSC 121 - Computer Programming II
COSC 121 - Computer Programming II
II
OOP Revision
The Basics
Default Values
- Data fields (object attributes or instance variables) can be of the following types”
- Primitive
- Example: int, double, etc
- Default values:
- 0 for a numeric type
- False for a boolean type, and
- \u0000 for a char type
- Reference types
- Example: string, arrays, or other class types
- Default values
- Null, which means that the data field does not reference
any object
Constructors
- Constructors play the role of initializing objects
- Constructors are a special kind of method
- They have 3 peculiarities
- Constructors must have the same name as the class itself
- Constructors do not have a return type – not even void
- Constructors are invoked using the new operator when an object is created
Array of Objects
- To create an array of objects, you need to follow two steps:
1. Declaration of reference variables:
a. You can create an array of objects, for example,
-
- A better solution is to have a superclass, e.g Shape, that has the common code
and then have Circle and Rectangle inherit from Shape
What is Inherited?
- When a subclass inherits (or extends) a superclass:
- Instance variable inheritance:
- All instance variables of the superclass are inherited by the subclass
- However, if a variable is private, it can only be accessed using methods,
defined by the superclass.
- Method Inheritance:
- All superclass methods are inherited by the subclass, but they may be
overridden.
Inheritance Example
What Can You Do in a Subclass?
- A subclass inherits from a superclass. You can:
- Use inherited class members (properties and methods)
- Add new class members.
- Methods:
- Override instance methods of the superclass
- To modify the implementation of a method defined in the
superclass
- The method must be defined in the subclass using the the same
signature and the same return type as in its superclass
- Hide static methods of the superclass
- By writing a new static method in the subclass that has the same
signature as the one in the superclass
- Constructors:
- Invoke a superclass constructor from within a subclass constructor
- Either implicitly
- Or explicitly using the keyword super
Overriding Methods
Overriding Methods
- Overriding allows a subclass to modify the behaviour of an inherited method as needed.
- I.e: provide a different implementation of a method that is already provided by the
superclass
- Overriding happens when you implement a method in a subclass that has the same
- Signature (name and parameters) and
- Return type (or subtype)
- as a method in its superclass
Overriding vs Overloading
- Overridden methods are in different classes related by inheritance
- Overloaded methods can be either in same class or different class
- Overridden methods has the same signature and return type
- Overloaded methods have the same name but a different parameter list
Example
- This example is based on the Circle class present a few slides ago
- In above output, we created a White, Filled circle, although these attributes were not
coded in the Circle constructor:
- The reason is, the Circle constructor calls the super constructor by default
Constructor Chaining
- Constructing an instance of a class invokes all the superclasses’ constructors along the
inheritance chain. This is known as constructor chaining.
“Final” Modifier
The “final’ Modifier
- A “final” local variable is a constant inside a method
- The “final” class cannot be extended:
Visibility Modifiers
- Access modifiers are used for controlling levels of access to class members in Java. We
shall study two modifiers:
- “Public”,
- The class, data, or method is visible to any class in any package
- “Private”,
- The data or methods can be accessed only by the declaring class
- If no access modifier is used, then a class member can be accessed by any class in the
same package
A Subclass Cannot Weaken Accessibility
- A subclass may override a protected method in its superclass and change its visibility to
public. However, a subclass cannot weaken the accessibility of a method defined in the
superclass. For example, if a method is defined as public in the superclass, it must be
defined as public in the subclass.
Polymorphism
Polymorphism Part A
What is ‘Polymorphism’?
- Term: A class defines a type. A type of a subclass is called a subtype, and a type of its
superclasses is a subtype
- E.g: Circle is a subtype of shape and shape is a supertype for circle
- Polymorphism: The ability of an object to take on “many forms”
- In Java, a reference variable of a supertype can refer to any of its subtype objects
- This allows us to perform a single action (method) in different ways
- More about this shortly
- Every instance of a subclass is also an instance of its superclass, but not vice
versa
- E.g: Every circle is a shape object, but not every shape is a circle
The Three Rules
- Rule 1: A reference of a supertype can be used to refer to an object of a subtype (not
vice versa)
- Rule 2: You can only access class members known to the reference variable
- Rule 3: When invoking a method using a reference variable x, the method in the object
referenced by x is executed, regardless, of the type of x
Polymorphism in Java
- A reference variable of a supertype can refer to any of its subtype objects, but not vice
versa
Passing References to Methods
Dynamic Binding and Rule #3
- Assume
- C1 is a subclass of C2, C2 is a subclass of C3,..., and Cn-1 is a subclass of Cn
- An object obj is an instance of C1 (and hence it is also an instance of C2,...,Cn)
- How does dynamic binding work?
- If we invoke a method obj.p(), the JVM searches the implementation for the
method p() in C1, C2,..., Cn-1 and Cn in this order, until it is found. Once an
implementation is found, the search stops and the first-round implementation is
invoked (RULE 3)
Polymorphism Part B
Generic Programming
- Writing methods that are sued genetically for different types of arguments
- Here we use polymorphism
- If a method’s parameter type is a supertype (e.g Object), you may pass an object
to this method of any of the parameter’s subtype (e.g Student or String).
- When an object is used in the method, the particular implementation of the
method of that object that is invoked is determined dynamically
Method Matching vs Dynamic Binding
- Matching a method signature and dynamically binding a method implementation are two
issues.
- Dynamic binding is used for overridden method and happens during the runtime, Here,
Java tries to bind the method call to a method definition up a class-inheritance hierarchy
- First searches a class for the method’s implementation then its parent(s).
- Static Binding: In case of methods overloading, the compiler finds a matching method
according method signature at compilation time
- Method signature = parameter type, number of parameters, and order of the
parameters
Exercise
Example
Project P1
2. Creating an array that contains objects of different but related types, all children
of one super type
instanceof Operator
- Use the instanceof operator to test whether an object is an instance of a class
- Imagine that you have two classes, A and B. And you have a reference x of a supertype
that points to an object of either A or B, but you don’t know which one. You may use this
code:
instanceof and Inheritance
- An object is an instance of its own class type and also of all its parent classes
Implicit Objects
- Casting objects refers to that one object reference can be typecase into another object
reference
Explicit Casting
- Suppose you want to assign the reference obj to a variable of a Student type as follows:
- Why error?
- Object obj is not necessarily an instance of Student. Even though you can see
that obj is really a Student object, the compiler is not so clever to know it
- To fix this error
- Tell the compiler that obj is a Student object, use explicit casting:
Example: Objects Casting + instanceof
- The displayShapeInfo method displays the diameter if the object is a circle and the
height & width if the object is a rectangle
Exercise
Caution
- For casting to be successful, you must make sure that the object to be case is an
instance of the subclass
- If the superclass object is not an instance of the subclass, a runtime ClassCastException
occurs
The Object’s equals Method
- The “==” operator is use to compare the references of the object. Comparing two
references to equality does not compare the contents of the objects referenced
- public boolean equals(Object O) is a method provided by the Object class. The default
implementation uses “==” operator to compare two objects as follows:
- It is expected that programmers will override this method so that it is used to compare
the values of two objects
- Try the following code with the current implementation of the Circle class (slide 6):
- The output is false as the two references c1 and c2 point to different objects. However,
we can override the equals method in the Circle class to compare the values in the
objects instead. The simplest implementation is as follows:
Abstract Classes
- Syntax:
- Abstract method have a header but not a body
- Syntax:
- Concrete classes (i.e not abstract) cannot contain abstract methods
- In a concrete subclass extended from an abstract class, all abstract methods
must be implemented (overridden), even if they are not used in the subclass
- If a subclass of an abstract superclass does not implement all the abstract
methods, the subclass must be defined abstract
- Why abstract methods
- Abstract methods are used to specify common behaviour for subclasses that
have different implementation of those methods
Interfaces
- An interface is a class-like construct that contains:
- Constants
- Methods: abstract, default, or static
- An interface defines common behaviour for classes (including unrelated classes) and
provide default implementation of some of that behaviour
- For example, you can specify that the unrelated objects are comparable, edible,
and/or cloneable using interfaces
is equivalent to
2) The above array uses references of the Object type, which means it can contain
instances of any class type
3) Since howToEat() and sound() are not methods of the Object, elements retrieved from
the array had ot be cast to the appropriate types using Edible or Animal.
Some Rules
- A class can implement several interfaces
- Accessing constants:
- A constant defined in an interface can be accessed using the dot operator along
with the name of the interface or the implementing class (or an instance of that
class)
Exercise
More on Interfaces
Interface Structure
- Interfaces have zero or more of the following items:
default Methods
- “default” methods provide default implementation in an interface
- Why need them? For backwards compatibility (i.e support adding new methods to
current interfaces without breaking the old code
static Methods
- “static” methods are implemented methods that are called using the interface name
private Methods
- “private” methods are accessible only within that interface
- Why?
- Default and static methods are public by default. If you want part of the code to
be accessible only in the interface, use private
Java Standard Interfaces
Comparable
- When you want to compare two objects, e.g two employees: is e1 > e2? The two
objects must be comparable
- Any class that implements the “Comparable” interface must have a method compareTo
- The “compareTo” method allows the class to define an appropriate measure of
comparison
- The Comparable interface is a generic interface
- The generic type T is replaced by a concrete type when implementing this interface
Comparable: Example
Comparable: Exercise
Cloneable
- In order to create a clone of an object (i.e, a field for field copy of the object) a class must
implement the Cloneable interface
- By convention, classes that implement this interface should overrible
Object.clone (which is protected with a public method)
A Few More Notes About Cloneable
- The Object’s clone method returns the type ‘Object’ which must be cased to the desired
type
Exception Handling
What are Java Exceptions?
- When a program runs into a runtime error, the program terminates abnormally. How can
you handle the runtime error so that the program can continue to run or terminate
gracefully?
- Exception handling enables a program to deal with exceptional situations and continue
its normal execution
- An exception is an object that represents an error or a condition that prevents execution
from proceeding normally
- Examples:
- A program tries to access an out-of-bound array;
- A program tries to connect to a website using an invalid address;
- A program runs out of memory
- A program tries to read a file of integers but finds a string value in the file
- If the exception is not handled, the program will terminate abnormally
Example
Java Exceptions
Unchecked Exceptions
- RuntimeException, Error and their subclasses
- They can occur anywhere in a program, usually because of programming logic errors
- To avoid cumbersome overuse of try-catch blocks, Java does not require you to
write code to handle them
- Example
- Subclasses of Error
- VirtualMachineError the JVM is broken or has run out of the resources it
needs in order to continue operating
- Subclasses of RunTimeException
- ArithmeticException: Dividing an integer by zero
- IndexOutOfBoundsException: Accessing an index (e.g of an array)
outside the valid limits
- NumberFormatException: When you try to convert a String to a numeric
value, but that the string does not have the appropriate format
- E.g int x = Integer.parselnt(s); //error if e.g, s = ‘abc’
- NullPointerException: Accessing an object through a reference variable
before an object is assigned to it
Checked Exceptions
- Exception subclasses except RuntimeException
- The compiler forces you to check and deal with them
- No default action when encountered (i.e Java doesn’t throw nor catch the
exception… Needs you to tell it what to do)
- Examples:
- Subclass of IOException
- EOFException occurs when a program attempts to read past the end of a
file
- FileNotFoundException can occur when a program attempts to open or
write to an existing file, but the file is not found
- MalformedURLException indicates that an invalid form of URL (such as a
website address) has occurred
Declaring Exceptions
- Declaring an exception causes the methods where the exception occurs to throw the
exception to the calling method
- To declare an exception in a method, use the keyword “throws” followed by the
type of exception in the method header
- If the method might throw multiple exceptions, use the following notation
Handling Exceptions
- To deal with an exception within a method, use “try-catch”
- Example: When this handler code is executed, the variable within the catch clause will
contain a reference to the exception object that has been thrown
- You can also catch an exception using a parent class of the throw exception
- Why is this possible?
- If more than one type of exception is possible, we can add additional catch clauses, like
this:
- Each catch block is examined in turn, from first to last, to see whether the type of the
exception object is an instance of the exception class in the catch block
- It is possible to have more than on try statement within a method. This clearly defines
the area of code where each exception is expected to arise
- The “finally” clause is always executed regardless whether an exception occurred or not.
It may be used to ‘clean up’ by releasing any resources such as memory or files that a
method has been using before the exception was thrown
Exception Propagation
- If no handler is found in a method, Java exits the method, passes the exception to the
method that invoked the method, and continues the same process to find the handler
- If no handler is found in the chain of methods being invoked, the program terminates and
prints an error message on the console
Example 1: Using Try-catch
Example 2: No Try-catch
Example 3: Using a Parent Exception Class
TextIO
Scanner
- Note that for this code to work properly, we need to write some error handling code to
handle IO exceptions
- Common Scanner Methods:
- Input methods:
- Test methods:
Caution
- Not having the right format in your text file may lead to errors. For example, both
programs below generate exceptions
Solution
- Solution 1: make sure you have the right format
- Not good enough. You need to protect your code from crashing!
- Solution 2: Use defensive programming or exception handling
PrintWriter
Text I/O
The File Class
- The file class provides an abstraction that deals with most of the machine-dependent
complexities of files and path names in a machine-independent fashion. The filename is
a string. The File class is a wrapper class for the file name and its directory path
- Some useful File methods:
- Exists(): boolean
- Returns true if the file or the directory exists
- canRead(): boolean
- Returns true if the file exists and can be read
- isDirectory(): boolean
- Return true if the File object represents a directory
- isFile(): boolean
- Returns true if the File object represents a file
Try-With-Resources
- Programmers often forget to close the file. JDK 7 provides the following new
try-with-resources syntax that automatically closes the files
Note: InputMismatchException is thrown by Java when the input does not match the type. It
can also be explicitly thrown by you whenever the input doesn’t match your own criteria, e.g:
Example 1
Example 2
Defensive Programming Solution
InputStream
- +read(): int
- Reads the next byte of data as int (0 to 255)
- -1 is returned at the end of stream
- +read(b: byte[]): int
- Reads up to b.length bytes into array b
- +read(b: byte[], off: int, len: int): int
- Stores read bytes into
OutputStream
- +write(int b): void
- Writes (byte) b to this output stream
- +write(b: byte[]): void
- Writes all the bytes in array b to the output stream
- +write(b: byte[], off: int, len: int): void
FileInputStream/FileOutputStream
FileInputStream
- Methods:
- All methods from InputStream
- Constructors
- public FileInputStream (String filename)
- public FileInputStream(File file)
- Exceptions
FileOutputStream
- Methods:
- All methods from OutputStream
- Constructors:
- public FileOutputStream(String filename)
- public FileOutputStream (File file)
- If the file doesn’t exist, a new file would be created
- If the file already exists, the current contents in the file are deleted
- public FileOutputStream(String filename, boolean append)
- public FileOutputStream(File file, boolean append)
- Used to retain the current content and append new data into the file
- Exceptions:
- Throws IOException
Exercise 1
More Exercises
Exercise Solutions
Exercise4 (hidden)
DataInputStream/DataOutputStream
DataOutputStream
- Wraps around FileOutputStream
- Converts primitive types or strings → bytes and output the bytes to the stream
- DataOutputStream out = new DataOutputStream(new FileOutputStream(“file.dat”));
- Methods: All methods of OutputStream in addition to:
DataInputStream
- Wraps around FileInputStream
- It reads bytes from the input stream → interprets binary data as primitive types or
strings
- DataInputStream in =
- new DataInputStream(new FileInputStream(“file.dat”));
- String chaining is a way of connecting several streams to get the data in the form
required
- Methods: All methods of InputStream in addition to:
Exercise
- Writes the number of bytes taken by s in the first two bytes, then s itself in
modified-UTF8 format
- Modified UTF8 stores a character using 1 or more bytes, depending on
the character
- ASCII characters are stores in 1 byte, Other characters may
require more than one byte
- The initial bits of a UTF-8 text indicate the length of the character
- Example
- After every group has been converted to a character by this process, the characters are
gathered, in the same order in which their corresponding groups were read from the
input stream, to form a String, which is returned.
Exercise
Checking End of File
- So how do you check the end of a file?
- Remember that DataInputStream is a descendent of InputStream → use
available() method to check the stream, input.available() == 0 indicates the end
of a file
Exercise
Efficiency?
- available() method is not very efficient method (i.e take some time to return result).
- An alternative way is to keep reading until your reader object fails to get any data.
- If you try to read past the end-of-life, DataInputStream will throw an exception
EOFException. We can use this in our code as follows:
ObjectInputStream/ObjectOutputStream
ObjectOutputStream
- Can be used to write serializable objects and primitive data
- Wraps FileOutputStream
- Converts primitive types or strings → bytes and output the bytes to the stream
- ObjectOutputStream out =
- New ObjectOutputStream(new FileOutputStream(“file.dat));
ObjectInputStream
- Can be used to read serializable objects and primitive data
- Wraps FileInputStream
- It reads bytes from the input stream → converts them to primitive types, strings,
objects
- ObjectInputStream in =
- new ObjectInputStream(new FileInputStream(“file.dat”));
Example 1:
Example:
Improving I/O Performance
BufferedInputStream / BufferedOutputStream
- Used to speed up I/O by reducing the number of disk reads and writes
- Methods
- All methods from InputStream/OutputStream
- Constructors
- public BufferedInputStream(InputStream.in) //bufferSize = 512
- public BufferedInputStream(InputStream in, intbufferSize)
- public BufferedOutputStream(OutputStream in) //bufferSize = 512
- public BufferedOutputStream(OutputStream in, intbufferSize)
Example: Using BufferedInputStream
Exercise
Tips
Example 1
- This recursive methods prints the numbers from n to 1
- Can you think of non-recursive ways of writing the print method
- Observations:
- Recursive code can be rewritten using loops
- Both include a condition to end the recursive call or loop
Stopping Condition
- Similar to loops, recursive methods need a “stopping condition” that determines when to
stop the ‘repetition’
Remember
- Recursion is a method that calls itself
- Recursive code can be re-coded with loops, but sometimes it is easier to use recursion
instead of loops
- Recursive methods include:
- A stopping condition (just like the condition that we use to control loops)
- A recursive call that involves a sub-problem resembling the original problem
Think Recursively!!
- With loops:
- ???
Think Recursively
- Fractals
- A fractal is a geometrical figure that can be divided into parts, each of which is a
reduced-size copy of the whole
- Example: let’s say we want to draw a fractal tree
- Sierpinski Triangle
1. Start with an equilateral triangle, which is considered to be the fractal of order (or
level) 0
2. Connect the midpoints of the sides of the triangle of order 0 to create a Sierpinski
triangle of order1
3. Leave the center triangle intact. Connect the midpoints of the sides of the three
other triangles to create a Sierpinski of order 2
4. You can repeat the same process recursively till you reach the desired order
- Fractal Art
Example 2: Factorial
- Factorial Defintion:
- The Fibonacci series begins with 0 and 1, and each subsequent number is the sum of
the preceding two
- Stopping conditions (Base cases)
- fib(0) = 0;
- fib(1) = 1;
- Recursive call
- fib(index) = fib(index-1) + fib(index-2); index>=2
- Recursion is not efficient, since it requires more time and memory to run the recursive
call. BUT the recursive implementation of fib method is very simple and straightforward.
Lets compare it to loops-based implementation
Summary
- What is a recursive method?
- A method that calls itself
- Recursive methods must have:
1. A recursive call (that calls the same method again)
a. Every recursive call reduces the original problem, bringing it closer to a
base case until it becomes that case
2. One or more base cases used to stop recursion
a. A stopping condition tells the method to stop calling itself when the
problem becomes in its simplest form (i.e the “base case”)
b. The method uses conditionals (e.g if-else, switch) to run different cases
- When to use recursion?
- When solving problems that have recursive structures
- Coding these problems recursively is simpler and more intuitive
- Any problem that can be solved recursively can be solved nonrecusrively (with
iterations) but recursion may provide simpler ways to solve the problem
More Practice
Practice 1
Practice 2
Practice 3
Practice 4
Recursion Part B
Example 1, again
- As we saw earlier, this method prints the numbers from n to 1
Example 1 (reordered)
- What is the output after reordering the statements?
Exercises
Some Problems that have a Nice Recursive Solution
- The Algorithm
- As long as there is a “remaining part”, do the following:
- Find the smallest element in the remaining part
- Swap with the current element
- Recursively repeat the process after ignoring the current element
Recursive Selection Sort
Practice 6
Tower of Hanoi
- Problem:
- There are n disks labeled 1,2,3,...,n and three towers labeled A, B, and C
- All the disks are initially placed on tower A
- It is required to mvoe all disks to tower B with the assistance of C while observing
the following rules
- No disk can be on top of a smaller disk at any time
- Only one disk can be moved at a time, and it must be the top disk on the
tower
Solution to Tower of Hanoi
- Base case (n = 1)
- If n ==1 simply move the disk from A to B
- Recursion
- Split the original problem into 3 subproblems and solve them sequentially:
1. Move the first n-1 disks from A to C recursively with assistance to B
2. Move disk n from A to B
3. Move n-1 disks from C to B recursively with assistance of A
Fractals
- After you have learned about recursion in Java, can you think of recursive methods to
draw the fractals below?
- Drawing in Java is not covered in this course!
Tail-Recursive Method
Recursion vs Iteration
Recursion vs Iteration
- Negative aspects of recursion:
- Recursion bears substantial overhead
- Each time the program calls a method, the system must assign space for
all of the method’s local variables and parameters
- This can consume considerable memory and may require extra time to
manage the additional space
- Positive aspects of recursion
- Using recursion may provide a clear simple solution for a inherently recursive
problem that are difficult to solve without recursion
- For example: Directory size, Tower of Hanoi, and Fractals
- When to use which
- Use whichever approach can best develop an intuitive solution that naturally
mirrors the problem
- If an iterative solution is obvious , use it. It will generally be more efficient than
the recursive option.
Recursion Efficiency
- Now imagine a much larger graph – see how many redundant calculations we need to
perform?
- We don’t really need to compute fib(i) more than once! The idea is, once fib(i) is
calculated once, we need to store it somewhere then read fib(i) whenever we need it
again (instead of re-calculating it).
- Here is how we do this. Let’s say that we have a recursive function F(parameters:
1. Create a global array M (outside F) that:
a. Has the same type as the recursive-method return type
b. Of dimensionality = number of recursive-method parameters
2. Before returning a result from F, store it in M at a index that match the parameter
values of F
3. Add one more stopping condition – if M has a value at indexes = F’s parameters,
then just return that value.
Array vs ArrayList
- Objects of the generic class ArrayList are similar to arrays, but have differences
- Similarities
- Both can store a number of references
- The data can be accessed via an index
- Differences
- ArrayList can automatically increase in capacity as necessary
- Will request more space from the Java run-time system, if
necessary
- ArrayList have methods to do many actions
- E.g can insert/remove elements anywhere in the list
- ArrayList is not ideal in its use of memory
- As soon as an ArrayList requires more space than its current
capacity, the system will allocate more memory space for it. The
extension is more than required for the items about to be added
- Can be simplified to
- Compiler is able to infer the type from the variable declaration
- list3 has references of type object, which means we can store any object types in it
Initializing an ArrayList
- Once you create an array list, you can start adding elements to it using its add method:
Some Methods
Practice Questions
Practice 1
Practice 2
The Stack
- A stack represents a LIFO (Last In First Out) data structure. The elements are accessed
only from the top of the stack. That is, you can retrieve insert, or remove an element
from the top of the stack
Practice 3
Sample Applications in Gaming
Practical Example
Practice 5
Practice 6
Experiment
Random Access in ArrayList
Experiment
Array<->ArrayList
- Array → ArrayList
- Creating an ArrayList from an array:
- Syntax: list = Arrays.aslist(array)
- ArrayList → Array
- Creating an array from an ArrayList:
- Syntax: list.toArray(array);
java.util.Collections Methods
Intro to Generics
Generics
- A generic class has at least one member of an unspecified type
- ArrayList is a generic class with a generic type E
- We have also seen generics before, e.g., the comparable interface
- The type(s) you provide on instantiation appear in the API as single letter in angle
brackets after the name of the class, e.g
- All collections support generic (or parameterized) types to indicate what type is stored in
the collection
- It is better to precisely specify the type of objects in a collection so that the compiler can
check for errors
- If you don’t, then the collection can store any type of object as all objects are a
subclass of object
- Java employed object wrapper, which ‘wrap around’ or encapsulate all the primitive data
types and allow them to be treated as objects
- Java automatically wraps 5.5 into new Double(5.5). This is called auto-boxing
- Examples:
Index or Value-of-item??
- When using generics, each PARAMETER MATCHING takes precedence over
AUTO-BOXING
- Consider the code below:
Collection
List Interface
- The List interface:
- Ordered (each element has an index)
- Duplicates allowed
- Methods:
- All methods from Collection
- “&” iterator methods: listIterator()
- “&” index-based methods:
- Concrete classes:
- ArrayList, LinkedList
ArrayList<E>
- Methods:
- All methods from List
- “&” extra method
- trimToSize()
- Trims the capacity of this ArrayList instance to be the list’s current
size
- Constructors
- ArrayList()
- Creates an empty list with default capacity
- ArrayList(int initialCapacity)
- ArrayList(c: Collection <? extends E>)
- Create ArrayList from an existing collection of any subtype of E
LinkedList<E>
- Methods:
- All methods from List
- “&” extra methods specifically for first and last elements
- Constructors
- LinkedList()
- Creates a default empty list
- LinkedList(c: Collection <? extends E>)
b. To create a general list (i.e can only use methods from List)
Example
Practice
Big-Oh Notation
- Big-Oh notation is a mechanism for quickly communicating the efficiency of an algorithm
- Big-Oh notation indicates the growth rate of a function (efficiency) when the size
of data (n) changes
- The letter O is used because the growth rate of a function is also referred
to as the “Order of a function”
- In big-oh notation:
- The performance is specified as a function of n which is the size of the problem
- E.g n may be the size of an array, or the number of values to compute
- Only the most significant expression of n is chosen:
- E.g if the method performs n^3 + n^2 + n steps, it is O(n^3)
-
- Constants are ignored for big-Oh:
- E.g If the method performs 5n^3 + 4n^2 steps, it is O(n^3)
- This is because we measure the growth rate, not the number of
executions, and hence both n and 10n will grow at the same rate
- Lets say you want to ONLY print elements of a stack. What is wrong with the highlighted
loop? How can you fix this?
- Answer: This code also destroys the stack. If you want to only print all elements you
need another technique that keeps the original stack intact and yet print the elements
- One way is to have a get(i) method – remember that a stack implements List
interface, so it has index-based operations
- The other way is to clone the stack first, then print th cloned stack using the code
above
Practice
Queues and Priority Queues
Queues
- A queue is a FIFO (first-in-last-out) data structure. Elements are appended to the end of
the queue and are removed from the beginning of the queue
Implementation of Queue
- LinkedList and PriorityQueue are two concrete implementation of Queue
- Example on using a LinkedList to implement to queue:
Practice
Caution!
- In priority queues, an iterator or (a for-each loop) is NOT guaranteed to transverse the
elements in any particular order. This is because it’s implemented as a priority heap
rather than sorted list
- The only guarantee provided by PriorityQueue is that poll(), peek() return the least
element
- How to show all elements according to their priority without actually remove them from
the priority queue?
- Solutions include
- Create a temporary priority queue and copy all elements from the original one to
the temp one, then use poll() in a loop
- Convert the priority queue to an array and use Arrays.sort
Today’s Challenge…
- Given:
- The code for MyList and MyAbstractList
- Required:
- Implement MyArrayList
- Implement MyLinkedList
MyList Interface
MyAbstractList Class
Implementing Linked Lists
Useful Illustrations
- Assume x and y are references to nodes in a linked list:
Practice: Add an Element to the End
- Solution:
1. Create a node and insert it to the list:
2. Create the second node and add it to the list
Implementing MyLinkedList
- In this part, we will implement the linked list shown below
ADDING
REMOVING
GETTING
The ITERATOR
- Here we need to implement the iterator of our MyLinkedList
1) Declare a method iterator in MyLinkedList
Testing MyLinkedList
Practice
- Given the linked list, RobotsList, which maintains information about the position of a
group of robots
Practice
Implementing ArrayLists
ArrayLists
- Array lists use fixed-size arrays
- Whenever you need to expand the capacity, create a new larger array to replace
the current array
- When inserting a new element into the array
1. First, ensure is enough room in the array
2. If not, expand:
a. Create a new array with the size twice as the current one +1
b. Copy the elements from the current array to the new array
c. The new array now becomes the current array
Insertion
- To insert a new element at a specified index
1) Ensure there is enough room for new element (if not, expand)
2) Shift all elements after the index to the right one
a) Obviously, if you are inserting at the end, there will be no shifting
3) Insert the element
4) Increase size by 1
Deletion
- To remove an element at a specified index
1) Shift all elements after the index to the left by one
2) Decrease size by 1
Implementing MyArrayList
- An arraylist uses an array of a generic type E, and has methods that allow inserting,
deleting, etc. below shows some of these methods
Ensuring There is Enough Room When Inserting
- Lets say that our array list has a private variable data
- Whenever you add one element, make sure there is enough space first, and expand as
needed:
REMOVING
SETTING, GETTING
MyArrayList
Testing MyArrayList
Practice
- Using composition: You can define an array list as a data field in the stack class,
and a linked list as a data field in the queue class
- Composition is better!
- Because it enables you to define a complete new stack or queue class without
inheriting unnecessary and inappropriate methods from the array/linked list
Remember: Stacks
- You have seen before how to implement a stack using array lists
Queues
- Similarly, you can implement a queue using a linked list
Sorting
Algorithms Efficiency
Demonstration
- Here are a few websites that can be used to demonstrate the different sorting algorithms
in this unit
Selection Sort
- Algorithm:
- For each element in the list:
- Find the smallest element
- Swap it with the element
- Repeat with the unsorted part
- Ignore the first element and apply the same algorithm on the
remaining smaller list
- Runtime efficiency
- Worst/Average/Best case: O(n^2). 2 nested loops
Insertion Sort
- IDEA: Start with a sorted list of 1 element. Repeatedly insert an unsorted element into a
sorted sublist until the whole list is sorted
- Algorithm:
- For each element e in the unsorted part
- Keep a copy of e
- Insert e it into the sorted list such that this list remains sorted
- By moving all elements larger than e forward by one step
- Runtime efficiency
- Worst/Average case: O(n^2). 2 nested loops
- Best case: O(n) for nearly sorted list
Insertion Sort, Another Solution
Merge Sort
- IDEA: Divide the array into two halves and apply a merge sort on each half recursively.
After the two halves are sorted, merge them
- Algorithm:
- Runtime efficiency
- Average/worst case: O(n log n)
Merge Two Sorted Lists
- Runtime efficiency
- Best/Average case: O(n log n)
- Worst case O(n^2) in rare situations
- When?
- In the best case the pivot divides the array each time into two parts of
about the same size
- In the worst case
- Pivot divides the array each time into one big subarray with the
other array empty. So the algorithm requires (n-1) + (n-2) + … + 2
+ 1 times = O(n^2)
- This happens when the smallest or largest element is always
chosen as the pivot
- Partitioning Algorithm:
1. Initialize pivot, low, high
2. while(low < high)
a. Search from both sides:
i. From left (low) for the first element > pivot
ii. From right (high) for first element <= pivot
b. Swap the elements at low <> high
3. Move pivot element at the correct location and return its index
How to Partition?
Practice Question
Improving a Quick Sort
- Better choice of the pivot
- Always choosing the smallest or largest element as the pivot leads to the worst
case scenario O(n^2)
- Can you think of a case which this could happen?
- Early versions of quicksort suggested choosing the leftmost
element as the pivot. What happens if the array is already sorted
(or reverse-sorted)?
- How to avoid the worst case scenario
- Choose a random index for the pivot
- Choose the middle element as the pivot
- Choose the median between the first, middle, and last elements
- Aim: to find a pivot that divides the array into 2 parts ot almost the
same size
- Called “median of three” partitioning → good estimate of the
optimal pivot
- Median-of-three works even with sorted or reverse-sorted array
- Other improvements were suggested
- Outside the scope of this course
- Java uses Dual-Pivot Quicksort technique
Radix Sort
- If K is too large, using the bucket sort is not desirable. Radix sort is based on bucket
sort, but uses only ten buckets
- IDEA:
- Divide the keys into subgroups based on their radix (base) positions
- Apply a bucket sort repeatedly for the key values on radix positions
- Apply a bucket sort repeatedly for the key values on radix positions, starting from
least-significant position
- Performance: O(dn)
- Where d is the max number of radix position
Practice Questions