CS62 - Spring 2021 - Class 8
Example code in this lecture
Interfaces
LinkedList
Lecture notes
admin
- quiz today
- Darwin due on Tuesday
- Remote support
interfaces
- There are two main ways we can indicate shared behavior. The first is through inheritance.
- The second is through interfaces. Interfaces define methods that a class must define. E.g.,
public interface InterfaceName{
public void method1();
public int method2(int number);
public ArrayList<String> anotherMethod(String myString);
}
(see ExampleInterface in
Interfaces code
)
- A class can then "implement" these methods by adding "implements" in the class definition:
public class A implements InterfaceName{
}
- we must then implement ALL of the methods described in the interface, or our code will not compile
- Why might this be useful?
- interfaces define a type, i.e., we can declare variables and parameters of that type
- allows us to group objects together with similar functionality
- public void example(InterfaceName obj){...}
- we know that we can call any methods defined in the interface from that object
- we can say, the only thing that's important to me is that the class implement method x, y and z
look at the SimpleInterface in
Interfaces code
- two methods
look at the NegativeNumber and PositiveNumber classes in
Interfaces code
- Two classes that both implement the SimpleInterface interface
- Notice that if I don't include one of the methods in the class, Java will complain
- I can also have other methods
look at the NumberUser class in
Interfaces code
- Like a class, interfaces define a type
- The methods that we can call on variables/parameters of that type are those defined by the interface
- Any object that implements that interface can be put into a variable/parameter of the interface type
- in the main method, we can call the useNumbers method with either a NegativeNumber object *or* a PositiveNumber object.
look at the BasicList interface in
Interfaces code
- defines a very basic interface for lists
- get/set
- add (add to the end)
- size
- like classes, we can also use generics on interfaces
look at the BasicArrayList class in
Interfaces code
- identical to the version we saw last class
- implements the interface
look at SimpleSearch.integerSearch in
Interfaces code
- We can specify the generic type for the interface (in this case an integer).
- Note that *any* class that implemented the BasicList interface could be used here
- Allows us to abstract away the implementation!
look at SimpleSearch.search in
Interfaces code
- Uses generics!
- If we have a static method and we want to use generics, we can define a type variable within the method header <E>
- this ensures the that the type of thing in the BasicList is the same as the thing that we're searching for
Java has a List interface for data structures that store data sequentially:
-
https://docs.oracle.com/javase/8/docs/api/java/util/List.html
- lots of methods, but key: get and set and particular indices
The general ArrayList class implements the List interface (
http://docs.oracle.com/javase/7/docs/api/java/util/List.html
)
- Which of the following methods are performed quickly by the ArrayList class (quickly being sub-linear)? Slowly?
- get
- set the value of an existing element
- add to the end
- contains/indexOf
- add at an index
- remove an element
- size
- Quick:
- get
- set
- add to the end (like we saw last time... amortized)
- size
- Slow:
- adding an element at a particular index
- have to shift everything after towards the end
- removing an element
- have to remove and shift everything after towards the front
- contains
- have to iterate over every item
No free lunch!
- there's no one best data structure
- to make certain operations fast we often have to compromise and make other operations slower
- depending on what operations are important, one data structure may be more appropriate than another
- we're going to start looking at different data structures that are good at different things
Linked lists
- linked lists are a *recursive* data structure
- the data structure is built out of nodes
- a node stores both a data item as well as a reference to another node
- draw a picture and compare and contrast a picture of an array
- how could we do this using a class?
- look at the Node class in
LinkedList code
- we'll use generics again to make the class general purpose
- two things that we keep track of (with private instance variables)
- private E data: the data that we're actually storing in the node
- private Node next: a link to the next node in our linked list
- we have methods that allow us to access and manipulate the data within the node
- as well as methods that allow us to traverse and manipulate the links
- We want to build a linked list data structure that supports similar operations to ArrayList (i.e., the List interface). How would we do it?
- we'll just store a link the head of the linked list
- all of our operations will start at the head and then modify/traverse the list appropriately
- look at the LinkedList class in the
LinkedList code
- only keep track of a single node, the head of the list
- Look at the two addFirst methods in
LinkedList code
- addFirst
- create the new node
- set the next value of that new node to head
- set head to be the new node
- addFirst2
- does the same things, but utilizing the Node constructor
- when we evaluate the right hand side head gets passed as the next parameter to the Node
- we then just store this new node directory as the head
- how would we remove the first element (assuming that there is at least one element)?
- look at removeFirst in
LinkedList code
- save the value of head so we can return it
- set head to be head.next()
- return the value of the original head
iterating through linked lists
- how can we iterate through all of the values in a linked list?
- how do we know were the end of the list is?
- it will be a node whose next value is null
- make a variable (it's often called "finger" and then move it down the list until we fall off of the end)
Node finger = head;
while (finger != null ) {
// do something with the current node, finger
finger = finger.next();
}
- do it recursively:
void iterate(Node current){
if( current == null ){
return null;
} else {
// do something with current
iterate(current.next());
}
}
write a method contains(E value). Returns true if the linked list has that value (based on .equals) and false otherwise
- look at contains in
LinkedList code
- start at the head and traverse through the nodes
- key: check if we've seen the node
- stop when we either find it or we run off the end
- at the end, see if we either found it or if we fell off the end of the list
- look at recursiveContains in
LinkedList code
- recursive data structures are often conducive to recursive method
- start at the head
- two base cases: we're either null or we've found it
- if neither of this, look at the next eleement
Which of these methods are fast/slow for singly linked lists?
- add to the end (slow)
- add to the front (fast)
- contains (slow)
- get (slow)
- insert at an index
- remove an element
- set the value of an existing element
- size
What does the singly linked list buy us?
- add and remove at the front of the list in constant time!
- what is the downside (no free lunch!)?
- get and set are now linear time operations