CS 1302 – Ch 21, Sets & Maps These notes cover Sections 5 and 6 of Chapter 21. Section 21.5 – Maps 1. A Map stores key/value pairs. The keys are like indexes used to access the values. A List uses an integer index to reference the items in the list. In a Map, the keys can be any objects. The 2. The Map Interface: java.util.Map<K, V> +clear(): void Removes all mappings from this map. +containsKey(key: Object): boolean Returns true if this map contains a mapping for the specified key. +containsValue(value: Object): boolean Returns true if this map maps one or more keys to the specified value. +entrySet(): Set Returns a set consisting of the entries in this map. +get(key: Object): V Returns the value for the specified key in this map. +isEmpty(): boolean Returns true if this map contains no mappings. +keySet(): Set<K> Returns a set consisting of the keys in this map. +put(key: K, value: V): V Puts a mapping in this map. +putAll(m: Map): void Adds all the mappings from m to this map. +remove(key: Object): V Removes the mapping for the specified key. +size(): int Returns the number of mappings in this map. +values(): Collection<V> Returns a collection consisting of the values in this map. 1 3. The Map hierarchy: «interface» java.util.Map<K, V> «interface» java.util.AbstractMap<K, V> java.util.SortedMap<K, V> +firstKey(): K java.util.HashMap<K, V> +lastKey(): K +comparator(): Comparator<? super K>) +HashMap() +headMap(toKey: K): SortedMap +HashMap(m: Map) +tailMap(fromKey: K): SortedMap java.util.LinkedHashMap<K, V> +LinkedHashMap() java.util.TreeMap<K, V> +LinkedHashMap(m: Map) +TreeMap() +LinkedHashMap(initialCapacity: int, loadFactor: float, accessOrder: boolean) +TreeMap(m: Map) +TreeMap(c: Comparator<? super K>) 4. Concrete Map implementations: a. HashMap – keys have no ordering. b. LinkedHashMap – Keys are ordered according to the order they were inserted. c. TreeMap – Keys are sorted (natural ordering, Comparable, or Comparator) 5. Advanced – The entries in a LinkedHashMap can also be accessed in the order in which they were last accessed, from least recently accessed to most recently (access order). This can be used in an LRU (Least Recently Used) cache. The constructor is: LinkedHashMap(initialCapacity, loadFactor, true). Section 21.6 – Case Study: Occurences of Words Study the example from the text carefully. Sections 21.7 – Singleton & Unmodifiable Collections & Maps Omit 2 Ch 20 & 21, JCF Summary 3 4 java.util.Collections List +sort(list: List): void Sorts the specified list. +sort(list: List, c: Comparator): void Sorts the specified list with the comparator. +binarySearch(list: List, key: Object): int Searches the key in the sorted list using binary search. +binarySearch(list: List, key: Object, c: Comparator): int Searches the key in the sorted list using binary search with the comparator. +reverse(list: List): void Reverses the specified list. +reverseOrder(): Comparator Returns a comparator with the reverse ordering. +shuffle(list: List): void Shuffles the specified list randomly. +shuffle(list: List): void Shuffles the specified list with a random object. +copy(des: List, src: List): void Copies from the source list to the destination list. +nCopies(n: int, o: Object): List Returns a list consisting of n copies of the object. +fill(list: List, o: Object): void Fills the list with the object. +max(c: Collection): Object Returns the max object in the collection. +max(c: Collection, c: Comparator): Object Returns the max object using the comparator. +min(c: Collection): Object Collection Returns the min object in the collection. +min(c: Collection, c: Comparator): Object Returns the min object using the comparator. +disjoint(c1: Collection, c2: Collection): boolean Returns true if c1 and c2 have no elements in common. +frequency(c: Collection, o: Object): int Returns the number of occurrences of the specified element in the collection. Arrays Class Methods Modifier and Type Method and Description static <T> List<T> asList(T... a) Returns a fixed-size list backed by the specified array. static int binarySearch(double[] a, double key) Searches the specified array of doubles for the specified value using the binary search algorithm. static <T> int binarySearch(T[] a, T key, Comparator<? super T> c) Searches the specified array for the specified object using the binary search algorithm. static float[] copyOf(float[] original, int newLength) Copies the specified array, truncating or padding with zeros (if necessary) so the copy has the specified length. static double[] copyOfRange(double[] original, int from, int to) Copies the specified range of the specified array into a new array. static void fill(float[] a, float val) Assigns the specified float value to each element of the specified array of floats. static void sort(double[] a) Sorts the specified array into ascending numerical order. static <T> void sort(T[] a, Comparator<? super T> c) Sorts the specified array of objects according to the order induced by the specified comparator. static String toString(double[] a) Returns a string representation of the contents of the specified array. 5 HashSet & HashMap with Custom Objects (Advanced) 1. If you want to use HashSet with custom objects then the custom class must override both the equals and hashCode methods. The Java API says that if two objects are to be considered equal (a.equals(b)==true) then their hashcodes must be equal (a.hashCode()==b.hashCode()). Note, this does not say that two objects that have the same hashcode are equal. The signature for the two methods are: public boolean equals( Object o ) public int hashCode() We already know what the equals method is used to enforce logical equality. For instance, two Employees are equal, for instance, if and only if they have the same SSN. 2. The hashCode method is a bit more involved to explain. You will study hashing in detail in the next course (CS 3410). For now, we say that the hashCode method returns aninteger that represents the location of the object. The HashSet class uses the hashcode to find an object in a set. The operation works something like this: in the course of doing certain operations (add, contains) if the HashSet class determines that two objects have the same hashcode, then it calls equals to see if the objects are really the same. 3. For instance, suppose we are using a HashSet to hold Employee objects. When we add an Employee to the HashSet, the Employee’s hashcode is used to store the object, like an index in an array. For example, an Employee object may have a hashcode of 48, so the HashSet add method would put this object in the 48th position in an array. Later, when you want to call the contains or remove method with that Employee object, the code retrieves the hashcode (48) for the object and then finds the object in position 48. The situation is identical with HashMap if we want to use custom objects for the keys or if we want to use the containsValue method when the values are custom objects. 4. So, how do we override the hashcode method? We will show a simple approach here and again in the HashMap section. You will consider this in more detail in CS 3410. To override hashcode effectively takes a much deeper understanding. Some further explanation and guidance can be found here: http://tutorials.jenkov.com/java-collections/hashcode-equals.html http://www.ibm.com/developerworks/library/j-jtp05273/ http://www.javaworld.com/article/2076332/java-se/how-to-avoid-traps-and-correctly-overridemethods-from-java-lang-object.html?page=2 http://www.javamex.com/tutorials/collections/hash_function_guidelines.shtml 6 5. Example – The class below only implements the equals method, but not the hashCode method. Code – Incorrect, only overrides the equals method. class EmployeeIncorrect { private String lName; private String fName; public EmployeeIncorrect( String lName, String fName ) { this.lName = lName; this.fName = fName; } public String getLastName() { return lName; } public String getFirstName() { return fName; } public boolean equals(Object o) { EmployeeIncorrect e = (EmployeeIncorrect)o; return ( this.getLastName().equals(e.getLastName()) ) && ( this.getFirstName().equals(e.getFirstName()) ); } } So, we create two Employees that are equal, but they both get added to the set. This is because the add method calls the hashCode method on each object and they are both different, so equals never gets called. The hashCode method that is called is the one implemented in the Object class which usually uses the address of the object to compute its hashcode. Usually this unique. Code Set<EmployeeIncorrect> set = new HashSet<EmployeeIncorrect>(); Output EmployeeIncorrect e1 = new EmployeeIncorrect("Jones", "Hugh" ); EmployeeIncorrect e2 = new EmployeeIncorrect("Jones", "Hugh" ); System.out.println( set.add( e1 ) ); System.out.println( set.add( e2 ) ); true true System.out.println( set ); [Hugh Jones, Hugh Jones] 7 6. Example – Here, we also override the hashCode method in the Employee class. Code class Employee { private String lName; private String fName; public Employee( String lName, String fName ) { this.lName = lName; this.fName = fName; } public String getLastName() { return lName; } public String getFirstName() { return fName; } public String toString() { return String.format("%s %s", getFirstName(), getLastName() ); } public boolean equals(Object o) { Employee e = (Employee)o; return ( this.getLastName().equals(e.getLastName()) ) && ( this.getFirstName().equals(e.getFirstName()) ); } public int hashCode() { int x = lName.hashCode(); int y = fName.hashCode(); int z = 23*x + 7*y; return z; } } Some test code now shows that the duplicate object will not be added Code Set<Employee> set2 = new HashSet<Employee>(); Output Employee e3 = new Employee("Jones", "Hugh" ); Employee e4 = new Employee("Jones", "Hugh" ); System.out.println( set2.add( e3 ) ); System.out.println( set2.add( e4 ) ); true false System.out.println( set2 ); [Hugh Jones] 8