Disadvantages of ArrayLists • We've seen that using an ArrayListis one way to represent a sequence. • But using an ArrayList here has disadvantages. • Most importantly, when an element is added or removed, the elements to the right have to be moved as well. • List implementations needn’t have this drawback.
Lists in general • To understand list implementations, it’s helpful to imagine an abstract notion of a list. • This sort of abstract thinking is helpful • we needn’t worry about implementation details
List as an abstract data type Abstractly, the list data type has operations like • addToEnd(E) • add(index,E) • get(index) • remove(index) • set(index,E) • size() • isEmpty() • Here E is an element and index specifies a position in the list.
Implementing the List ADT • It’s possible to implement such an abstract data type (ADT) List either in terms of • an array, or • a collection of nodes where each node has a link to its predecessor or successor
Linked lists • In the linked case, suppose the place where an item is to be added or removed is known. • Then it can be added or removed by looking only at the nodes on either side of that place. • we skip the details (from Section 15.2), since they are covered in CS 46B. • This linked implementation is known as a linked list.
Efficiency and linked lists • You needn't understand implementation details in order to use a list implementation. • But you should know the relative efficiency of each fundamental operation. • As summarized on p. 574 of the text • Here O(1) to means bounded time, and O(n) means time proportional to the list size.
Issues in measuring efficiency • Strictly speaking, we should distinguish between worst-case time and average-case time. • But for the three operations on p. 574, these times are always the same. • With these three operations to start with, we can evaluate more complicated operations. • again we leave the details for CS 46B.
Lists and linked lists in Java • In Java, the List ADT is represented by a List interface. • It has all of the list methods given above. • except that addToEnd(E) becomes add(E) • The ArrayList and LinkedList classes implement this interface. • The LinkedList class uses a linked implementation of lists. • so it has the advantages and disadvantages of this implementation.
The LinkedList class • The LinkedList class is generic, like ArrayList. • It has several important methods of its own, that aren’t part of the interface. • Some of these methods would be very inefficient for ArrayLists.
The LinkedList class • Methods particular to LinkedListinclude (cf. p. 557) • addFirst(E) • addLast(E) • getFirst() • getLast() • removeFirst() • removeLast()
Iterators • Normally, linked lists are processed (traversed) by means of an iterator. • the intuition behind the use of iterators is the similar to that behind scanners • A new iterator can be created by sending an iterator message to a linked list. • An iterator accepts the messages next(), hasNext(), and remove() • Iterator is a generic interface, and so should be used with a class as parameter.
Iterators and the LinkedList class • The LinkedList class actually supports a bidirectional iterator. • To get a bidirectional iterator for a linked list, send it the listIterator()message. • This iterator accepts all Iterator and ListIterator messages, notably • previous() • hasPrevious() • add(E) • remove()
Behavior of Iterators • Iterators can be used for straightforward traversal, • as at the bottom of p. 558 of the text. • However, they can be used more generally. • For example, they can be used to remove elements that don’t satisfy a condition. • The enhanced for loop cannot do this • although it can be used for simple traversal of a list
Intuitions for iterators • It’s most useful to think of iterators as pointing between consecutive elements of a list. • The next() and previous()methods both move the iterator past a list element, but in different directions. • The remove and set methods both operate on the last element that was returned by a call to next or previous.
Stacks • Stack is an abstract data type with insertion and deletion operations that are inverses. • That is, the item deleted is the item most recently inserted. • This property is often called the LIFO (last in, first out) property. • The insertion operation is conventionally called push, while the deletion operation is conventionally called pop.
Queues • Queue is an abstract data type with insertion and removal operations for which the item deleted is the item least recently inserted. • This property is often called the FIFO (first in, first out) property.
Priority queues • PriorityQueue is an abstract data type with insertion and removal operations. • Here the item deleted is the item with the highest priority.
Applications of stacks • Stacks are used to store postponed portions of method or function calls. • Note that the LIFO property is just what is wanted here – a called method should finish before its caller can finish.
Applications of queues and priority queues • A priority queue is typically used to store events waiting for a resource such as a printer. • Here the event waiting the longest should perhaps not be the one that gets the next chance at the resource. • A queue is a special case of a priority queue where the longest waiting event should be the winner.
Stacks in Java • Java has a (generic) Stack class, with push and pop methods. • It implements Iterable, Collection, and List.
Queues in Java • Queues can be implemented in Java in terms of LinkedLists, by adding at one end and removing from another. • The sample implementation of p. 577 of the text adds at the end and removes from the beginning.
Priority queues in Java • Java has a (generic) PriorityQueue class. • it implements Iterable and Collection • It uses compareTo (or compare) to compare priorities. • an earlier value in the ordering corresponds to a higher priority • Insertion (of an element of type E) and removal correspond to the messages • add(E) and remove() • There is also a 1-argument remove method.
Sets and Maps • Two important abstract data types are represented by generic interfaces in java.util. • the Set interface represents sets • the Map interface represents functions • Two implementations are provided in each case.
Sets in Java • A set does not allow repeated elements. • so insertion of an item already in a set should fail • It’s an unordered collection, so items can’t be added at a particular numbered position. • Set’s add method returns false if the item was already present; true otherwise. • The remove method returns true if the element was present, and false otherwise. • i.e., both methods return true if and only if the set was modified
Iterating over sets • The iterator method returns an iterator that in general may iterate over the set in an arbitrary order. • You can still use iterators to remove -- but not add --at the position of the iterator. • There's no notion of iterating in reverse over sets.
The Set interface • The Set interface extends Collection, and provides all of its methods. • In particular, it provides a boolean-valued contains method. • This method may take an Object as argument. • It uses this object's equals method to check for membership.
The Map interface • The Map interface represents functions from a key type to a value type. • Map types need two parameters. • For example, a data structure that can be used to look up banks by their name could be of interface type • Map<String, Bank>
Methods of the Map interface • Maps have a method containsKey that checks whether a given object exists as a key in the map. • A containsValue method behaves similarly. • The put method of Map takes a key and a value and adds the association between the two to the map. • if the key is already associated with another value, that association will be overwritten.
Methods of the Map interface • The get method for Map takes a key and returns the associated value if there is one • and null otherwise • The remove method takes a key and removes it and any associated value from the map. • Both put and remove return the old associated value if there was one, • and null otherwise
Maps and collections • Maps are not collections. • But from a map, you can get • the set of keys (use keySet) • the collection of values (use values) • the set of key-value pairs (use entrySet) • these pairs are of interface type Map.Entry. • The methods getKey and getValue return the key and value from an entry of this type. • The collection of values can’t be a set • it may contain repeated elements
Implementing Set and Map • Both Set and Map have hashed implementations and tree implementations. • The implementing classes are HashSet, HashMap, TreeSet, and TreeMap. • The first two give fast insertion and lookup. • Iteration traverses a TreeSet in sorted order • if the element type implements Comparable, or • if it was constructed with a Comparator object. • The same applies to key sets of a TreeMap.
Hashed implementations • In hashed implementations, the hashCode method must be defined appropriately for the element or key type. • This method takes an Object and returns an int. • For reasons given in Section 16.3 of the text, the function value should be equally likely to be any int.
Defining hashCode • Normally an object’s hashCode value is defined in terms of the hashCode values of its instance variables. • Section 16.4 suggests how to do this. • You should make sure that equal elements get the same hash code value.
Recursion • Recursion simply means that a function or method calls itself • perhaps indirectly • Recursion is your friend! • Specifically, it simplifies the important design technique of divide and conquer
Divide-and-conquer problem solving • A divide-and-conquer algorithm works by • dividing the input into pieces • processing each piece • combining the processed pieces • If there are no pieces, the input is likely small • and processing it is likely simple • Pieces of the same type as the original input may be processed recursively • using the same method being defined • so no new method is needed!
Binary search trees (BSTs) • Binary search tree (BST) is a data type used to implement TreeSets and TreeMaps. • A BST is empty, or has three pieces • a root data item, a left subtree (LST), and a right subtree (RST) • the LST contains all set members that precede the root in the ordering of the element type • the RST contains all members that follow the root • if the element type isn’t Comparable, an ordering must be specified by a Comparator object.
Binary search tree operations • Simple divide-and-conquer algorithms exist for BST search, insertion, and deletion. • In the worst case, these algorithms take time proportional to the height of the tree • the largest distance from the root to a member • TreeSet and TreeMap operations modify the tree as needed to keep its height small • search is still simple • it’s helpful to understand the search algorithm when debugging TreeSets and TreeMaps
Generic programming in Java • Generic programming is "the creation of programming constructs that can be used with many different types". • Two strategies are available for generic programming in Java. • The earliest was the use of inheritance. • A collection with no specified element type contains Objects as elements • so these elements can be objects of any class.
Parametrization • We have now also seen a second strategy -- parametrization (with type variables), e.g. • ArrayList<String> • TreeMap<String, Integer> • To declare a generic class, a type variable is used for the parameter (cf. Syntax 17.1), e.g. • ArrayList<E> • TreeMap<K, V> • Recall that this variable must be instantiated with a class and not a nonclass type.
Type variables • The big advantage of using type variables is type safety. • Errors that would be run-time errors with inheritance are compile-time errors in the parametrized case. • Code is also easier to read when desired element types are given explicitly.
Using and defining generic classes • Using generic classes is not particularly difficult. • Defining generic classes, as opposed to generic methods, is fairly easy • the text's Pair class is a simple example • its LinkedList class is still straightforward • these examples are simple because they and their methods work with all types. • In general, use type variables as you would use parameters in a method definition.
Constraining the type variable • In some cases, it makes sense to bind the type variable only to a limited range of types. • For example, you might want to limit a genericminimummethod toComparableclasses.
Generic methods • The syntax for the simplest generic methods is only slightly more elaborate than in the nongeneric case. • A generic method that is to apply to all classes needs to have the type variable, inside angle brackets, before the return type. • Such a class is the ArrayUtil.printmethod of p. 729 of the text.
How to constrain the type variable • If permissible types are to be constrained, the type variable has to be constrained as well. • To limit a generic minimum method to Comparable classes, make the type variable • <E extends Comparable> • rather than just <E>
Limitations on generics in Java • It's worth knowing that Java erases type variables after compilation. • This helps explain some otherwise puzzling limitations on their use compilation • cf. Section 17.5 of the text • In particular, to interface with legacy code, raw types are needed • i.e., types without type variables
More limitations • Another surprising limitation on genericity is described as Common Error 17.1, p 731. • Collection<S> is not a subclass of Collection<T> just because S is a subclass of T. • Dealing with this requires another way to constrain types • the wildcard types of Special Topic 17.1, p 731.
Wildcards • Recall that the parameter type of the add method of the Collection<E> is just E. • so an object of any subclass of E may be added • But a Collection<E> parameter for addAll would not allow adding all of the elements of a collection of such instances. • the parameter is Collection<? extends E> • This parameter describes a collection whose elements may be of any subclass of E. • Eis a lower bound for the permitted types.
Upper bounds • Upper bounds may be needed as well. • For example, consider the TreeSet<E> constructor that takes a Comparator object. • This constructor should be happy with a Comparator object that compares elements of any superclass of E. • The way to specify such a Comparator object is with the parameter type • Comparator<? super E>
Unbounded wildcards • The use of a third type of wildcard, the unbounded wildcard, is illustrated by the removeAll method of Collection<E>. • here the argument can be any collection • its element type needn't be related to E • So the parameter type is Collection<?>. • Note that this method may throw a ClassCastException.