class: left, middle, title-slide # CSCI-UA 102 ## Data Structures
## Lists (Part 4) .author[ Instructor: Uday Kusupati
] .license[ Copyright 2020 Joanna Klukowska. Unless noted otherwise all content is released under a
[Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/).] --- layout:true template: default name: section class: inverse, middle, center --- layout:true template: default name: poll class: inverse, full-height, center, middle --- layout:true template: default name: breakout class: breakout, middle --- layout:true template:default name:slide class: slide .bottom-left[© Joanna Klukowska. CC-BY-SA.] --- template: section # Comparing `ArrayList
` Objects for Equality --- ## `equals` method specification The `equals` method in the `ArrayList
` class is inherited from the `AbstractList
` class. ---- -- `public boolean equals​(Object o)` Compares the specified object with this list for equality. Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements `e1` and `e2` are equal if (`e1==null ? e2==null : e1.equals(e2))`.) In other words, two lists are defined to be equal if they contain the same elements in the same order. __Implementation Requirements__: This implementation first checks if the specified object is this list. If so, it returns `true`; if not, it checks if the specified object is a list. If not, it returns `false`; if so, it iterates over both lists, comparing corresponding pairs of elements. If any comparison returns `false`, this method returns `false`. If either iterator runs out of elements before the other it returns `false` (as the lists are of unequal length); otherwise it returns `true` when the iterations complete. __Parameters__: `o` - the object to be compared for equality with this list __Returns__: `true` if the specified object is equal to this list -- .box[ Note that this specification allows us to use `equals` to compare different implementations of a list, for example an `ArrayList` instance with a `LinkedList` instance. The only requirement is that they contain the same elements in the same order. ] --- ## `equals` method implementation This is the code from `AbstractList
` class. ```java public boolean equals(Object o) { if (o == this) //check if the specified object is this list return true; if (!(o instanceof List)) //check if the specified object is a list return false; //iterate over both lists, comparing corresponding pairs of elements ListIterator
e1 = listIterator(); ListIterator> e2 = ((List>) o).listIterator(); while (e1.hasNext() && e2.hasNext()) { E o1 = e1.next(); Object o2 = e2.next(); if (!(o1==null ? o2==null : o1.equals(o2))) return false; } return !(e1.hasNext() || e2.hasNext()); } ``` --- ## `equals` method alternative implementation This is an alternative implementation of the same method. ```java public boolean equals(Object o) { if (o == this) //check if the specified object is this list return true; if (!(o instanceof List)) //check if the specified object is a list return false; //check if lists are of the same length if (this.size() != ((List>) o).size() ) { return false; } //iterate over both lists, comparing corresponding pairs of elements for (int i = 0; i < this.size(); i++ ) { E o1 = this.get(i); Object o2 = ((List>) o).get(i); if (!(o1==null ? o2==null : o1.equals(o2))) return false; } return true; } ``` -- What is the performance of this function? --- template:section # `ArrayList
` Summary --- ## `ArrayList
` - __Capacity__ of an `ArrayList
` is the size of the actual array used for storing data. - __Size__ of an `ArrayList
` is the number of elements that have been stored in it. $$ Capacity \geq Size $$ - An empty list will often have a non-zero capacity. - Capacity is not decreased as elements are removed (could be, but the `ArrayList
` class does not do it). But it can be reduced by an explicit call to `trimToSize()` -- __Amortized Performance__ .center80[.small[ | | front | back | index in the middle | |---|---|---|---| |adding| O(N) | O(1) | O(N) | |removing| O(N) | O(1) | O(N) | ]] -- __Performance for other functions__ .left-column2[.small[ |function | performance | |:---:|:---:| |`get(index)` | O(1) | |`set(index,element)`|O(1)| |`size()` | O(1) | |`isEmpty()` | O(1) | |`clear()` | O(N) .small[resets each value to `null`] | ]] .right-column2[.small[ |function | performance | |:---:|:---:| |`contains(object)`|O(N)| |`indexOf(object)`|O(N)| |`equals()` | O(N) | |`toArray()` | O(N) | |`sort()` | O(N logN) | ]] --- template:section # Examples and Things to Think About --- ## Implementing `contains` and `indexOf` Without looking at the source code of the `ArrayList
` class, implement the `contains` and `indexOf` methods. Use the documentation to find out exactly what they are supposed to do. Once you have your code, compare it to the one in the `ArrayList
` class and see how similar/different it is. - Is it equivalent (i.e., does your do the same thing that the `ArrayList
` code does )? - Does it have the same performance? --- ## Binary search on an `ArrayList
` The `contains()` and `indexOf` methods provides a linear search for the `ArrayList
`. But we know that a binary search is much more efficient if the data is sorted. - Write a method `boolean isSorted (ArrayList
list )` that determines if the given list is sorted or not. - Write a method `int binSearch(ArrayList
list, E key)` that performs the binary search and returns the index of `key` or -1 if the `key` is not present in the `list`. - Write a method `int bestSearch(ArrayList
list, E key)` that first determines if the list is sorted or not, and selects to either use `binSearch` or `indexOf` method accordingly. What is the performance of this method? Is it better, worse or the same as the performance of the `indexOf` method? -- Note that the above methods are NOT part of the `ArrayList
` class. They should be implemented as stand alone methods. --- template: section # Other _Flavors_ of a Linked List --- ## Other _Flavors_ of a Linked List The list that we discussed extensively is a __singly linked list__. In that list - there is a single reference from a node to the node that follows, and - the last node's reference is set to `null`. -- In a __doubly linked list__ there is a double connection between the nodes: each node has a reference to the node that follows and to the node that precedes it. -- In a __circular linked list__ the last node is connected back to the first node (instead of having its reference set to null). Circular linked lists could be singly- or doubly linked. --- ## Doubly Linked List .center[
] -- .right-column2[ ```java class Node
{ E data; Node
next; Node
prev; } ```
] -- - Working with a doubly linked list is very similar to working with a singly linked list, but there are twice as many references to keep track of. -- - Some operations become more efficient. For example, removing the last node, since `tail.prev` provides the reference to the node before last and it does not require iterating through the entire list. -- - Java's `LinkedList
` class uses a doubly linked list implementation. --- ## Circular Linked List .left-column2[
] .right-column2[ Circular __singly__ linked list. - there is a single reference in each node pointing to the next node - the last node's reference, points back to the first node ] -- .below-column2[ ] .left-column2[
] .right-column2[ Circular __doubly__ linked list. - there two references between a pair of any nodes: one pointing forward, the other pointing back - the last node's `next` reference, points back to the first node - the first node's `prev` reference, points to the last node ] --- template: section # Defining and Iterator for our LinkedList --- ## Why do we need iterators? An iterator is an object that allows us to traverse a collection (data structure) and visit each element exactly once. __Objective:__ be able to return the _next_ element fast, i.e., in O(1), so that the entire collection can be traversed in O(N) time. --- name: why-arraylist ## Why do we need iterators? Here is an example of iterating over an `ArrayList
` object using an ordinary `for` loop and the `get(index)` method and using an iterator. ```java ArrayList
aL = new ArrayList<>(); String [] strings = {"hello", "big", "pink", "cat"}; for (int i = 0; i < strings.length; i++) { aL.add(strings[i]); } System.out.println("Using for loop with .get() method" ); for (int i = 0; i < aL.size(); i++ ) { System.out.println(aL.get(i)); } System.out.println("Using an iterator" ); Iterator
itr = aL.iterator(); while (itr.hasNext()){ System.out.println(itr.next() ); } ``` -- What is the performance of each method? - using or loop with .get() method - using an iterator --- template: why-arraylist What is the performance of each method? - using or loop with .get() method __O(N)__ - using an iterator __O(N)__ --- name: why-linkedlist ## Why do we need iterators? Here is an example of iterating over an `LinkedList
` object using an ordinary `for` loop and the `get(index)` method and using an iterator. ```java LinkedList
lL = new LinkedList<>(); String [] strings = {"hello", "big", "pink", "cat"}; for (int i = 0; i < strings.length; i++) { lL.add(strings[i]); } System.out.println("Using for loop with .get() method" ); for (int i = 0; i < lL.size(); i++ ) { System.out.println(lL.get(i)); } System.out.println("Using an iterator" ); Iterator
itr = lL.iterator(); itr = lL.iterator(); while (itr.hasNext()){ System.out.println(itr.next() ); } ``` -- What is the performance of each method? - using or loop with .get() method - using an iterator --- template: why-linkedlist What is the performance of each method? - using or loop with .get() method __O(N
2
)__ (because the `get()` method takes O(N) time) - using an iterator __O(N)__ --- ## How to Implement an Iterator for a Linked List Here is a __very simple__ iterator that satisfies the [`Iterator
` interface](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Iterator.html): ```java private class Itr implements Iterator
{ private Node current = head; public boolean hasNext() { return current != null; } public E next() { if (current == null ) throw new NoSuchElementException( "no elements left to visit"); E tmp = current.data; current = current.next; return tmp; } } ``` -- and to make the list itself `Iterable`, we need to add the following method to the list ```java public Iterator
iterator() { return new Itr(); } ``` --- template:section # Examples and Things to Think About --- ## `LinkedList
` source code Study the source code of a doubly linked list implementation provided by the `LinkedList
` class in Java. You should be able to understand what it does and why. --- ## Is it circular? Given a reference to the first node, i.e. `head` determine if the list is circular or not. - case 1: assume that there is a `tail` reference pointing to the last node - case 2: assume that there is no `tail` reference What is the performance of these two methods. --- ## Does it have a loop? Given a reference to the first node, i.e. `head` determine if the list has a loop or not (a loop in a list means that the last node points back to another node in the list). .center[
] --- ## What will this code output? Assume that we execute the following code fragment. What is the output? ```java ArrayList
aL = new ArrayList<>(); String [] strings = {"hello", "big", "pink", "cat"}; for (int i = 0; i < strings.length; i++) { aL.add(strings[i]); } Iterator
itr = aL.iterator(); System.out.println(itr.next() ); itr.next(); System.out.println(itr.next() ); Iterator
itr1 = aL.iterator(); Iterator
itr2 = aL.iterator(); System.out.println(itr1.next()); System.out.println(itr2.next()); System.out.println(itr.next()); ```