/** * Sandeep Desai s_desai@hotmail.com http://www.thedesai.net * * Collections.java * Sample program to demonstrate Java 1.4 Collections features * * Book Reference: Thinking in Java 3rd Ed **/ /** * JDK 1.0 Collections * Vector, Hashtable methods are synchronized (slow) * * New JDK 1.2 Collections (not synchronized) * * Collection holds objects only * Set cannot have duplicates, List can have duplicates * Map is a Hash Table (Associative Array) * Collections can be ordered (store/retreive in specific order) or sorted * In inheritance tree below leaf level items are class * everything else is interface * Note Collection interface and Collections class different * * Collection can be ordered, retrieval order should match insert order * i.e iterate over the collection in a specific order * Collection can be sorted by natural order * * java.util.Collections class provides methods to * sort, binary search, get synchronized list, min, max, rotate, * get immutable list * * equals(null) should not throw nullpointer exception it should return false * hashcode maybe negative * * * +Collection * | * +---+Set * | | * | +--HashSet (provides constant time performance, can have null keys) * | +--LinkedHashSet (ordered set) * | +--+SortedSet (Interface) * | +--TreeSet * | * +---+List * | * +--Vector (old Java 1.0 synchronized) * +--ArrayList * +--LinkedList (doubly linked list methods add/remove first/last) * * +Map * | * +---Hashtable (old Java 1.0 synchronized) extends abstract class Dictionary * +---HashMap (can have null values and null keys) * +---LinkedHashMap (ordered HashMap) (retrieve entries in order inserted) * + (has accessOrder parameter for LRU or MRU) * +---IdentityHashMap (compares using == instead of equals, violates HashMap contract) * +---WeakHashMap (key removed when not in use) * +---+SortedMap * | * +--TreeMap * * * interface Collection * return true if collection changed for methods below * add(Object o) remove(Object o) * addAll(Collection c), removeAll(Collection c) * * clear() * boolean contains(Object obj) * boolean containsAll(Collection c) * boolean isEmpty() * Iterator iterator() * retainAll(Collection c) // true if collection changed * int size() * Object[] toArray() * * interface Set extends Collection // no methods added * * interface List extends Collection * get(int index) indexOf(Object o) * * interface Iterator * hasNext(), next() , remove() * * interface ListIterator extends Iterator * hasPrevious() previous() nextIndex() previousIndex set(Object o) * * * Map does not implement Collection. * Dictionary is a class, not an interface. * Collection is an Interface where as Collections is a helper class. * .equals() returns false if the object types are different. * It does not raise a compiler error for null **/ import java.util.*; public class JavaCollections { public static void arrays() { int[] a1 = new int[2]; Person[] as = new Person[2]; as[0] = new Person("one", 11); // holds reference to String object as[1] = new Person("two", 12); // initialize all array elements to 1, fill available for all // primitive data types and objects Arrays.fill(a1, 1); int[] a2 = new int[2]; boolean b = Arrays.equals(a1, a2); // Are two arrays equal String[] s1 = { "zaa", "bar", "foo" }; String[] s2 = new String[3]; // make copy of array makes a shallow copy and copies references only System.arraycopy(s1, 0, s2, 0, 3); Arrays.sort(s1, new StringComparator()); Arrays.sort(as); // throw exception ??? // sort using the comparator in Person Arrays.sort(as, new Person("temp", 1)); // binary search on a sorted array, returns >= 0 if found // or negative number which is index where element could be inserted int index = Arrays.binarySearch(s1, "bar"); // returns 2 index = Arrays.binarySearch(s1, "car"); // returns -2 List list = Arrays.asList(s1); // Convert array to collection } public static Person[] getPersons() { return new Person[] { new Person("one", 11), new Person("two", 12), new Person("three", 13) }; } // legacy collection objects since Java 1.0 are Vector, Hashtable, Stack public static void vector() { Vector v = new Vector(); v.add("one"); v.add("two"); v.add("three"); for (Iterator it = v.iterator(); it.hasNext();) { String s = (String) it.next(); if (s.equals("two")) it.remove(); } System.out.println(v); v.removeAllElements(); } public static void hashTable() { Hashtable h = new Hashtable(); // Associate any two objects in a key-value pair h.put(new Person("one", 11), "a"); // we put a string here Object two = new Person("two", 12); h.put(two, new Person("b", 13)); // we put an object here h.put("three", "c"); h.put(new Integer(4), "d"); // for primitive data type use wrapper for (Enumeration en = h.keys(); en.hasMoreElements(); ) { Object key = en.nextElement(); Object value = h.get(key); } } public static void stack() { Stack s = new Stack(); s.push("a"); s.push("b"); String b = (String) s.pop(); s.clear(); } // Collections added in Java 1.2 should be used instead of legacy // collections Vector, Hashtable, Stack public static void collection(Collection coll) { System.out.println("in collection"); Person[] pa = getPersons(); coll.add(pa[0]); coll.add(pa[1]); coll.remove(pa[0]); boolean b = coll.contains(pa[1]); // calls Person.equals() int size = coll.size(); for (Iterator iter = coll.iterator(); iter.hasNext(); ) { Person p = (Person) iter.next(); iter.remove(); } //Person[] p2 = (Person[]) coll.toArray(); throws exception coll.clear(); // remove all elements } // Set implements Collection no duplicates allowed public static void set(Set set) { System.out.println("in set"); Person[] pa = getPersons(); set.add(pa[0]); set.add(pa[1]); // will call Person.hashCode() to determine that this is a duplicate set.add(pa[0]); System.out.println("set size=" + set.size()); } public static void linkedHashSet() { LinkedHashSet lhs = new LinkedHashSet(); set(lhs); lhs.iterator(); } public static void main(String[] argv) { arrays(); vector(); hashTable(); stack(); ArrayList al = new ArrayList(); collection(al); // HashSet is a set backed by a Hash table HashSet hs = new HashSet(); set(hs); LinkedHashSet lhs = new LinkedHashSet(); set(lhs); linkedHashSet(); } } /** * Comparator is a Strategy Pattern */ class StringComparator implements Comparator { public int compare(Object o1, Object o2) { String s1 = (String)o1; String s2 = (String)o2; return s1.toLowerCase().compareTo(s2.toLowerCase()); } } ///:~ // simple Person object to illustrate usage of collections // and to show that if an object uses the Set or Map collection it // should implement equals and hashCode class Person implements Comparable, Comparator, Cloneable { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } /** * we just use the String.equals, your object should implement equals * on your rules * The equals method implements an equivalence relation on non-null * object references: * It is reflexive: for any non-null reference value x, * x.equals(x) should return true. * It is symmetric: for any non-null reference values x and y, * x.equals(y) should return true if and only if y.equals(x) returns true. * It is transitive: for any non-null reference values x, y, and z, * if x.equals(y) returns true and y.equals(z) returns true, * then x.equals(z) should return true. * It is consistent: for any non-null reference values x and y, * multiple invocations of x.equals(y) consistently return true or * consistently return false, provided no information used in equals * comparisons on the objects is modified. * For any non-null reference value x, x.equals(null) should return false **/ public boolean equals(Object obj) { System.out.println("in Person.equals"); if (obj instanceof Person) // if obj is null instanceof returns false { Person person = (Person) obj; return person.name.equals(name) && person.age == age; } else return false; } /** * From interface Comparator * Compares its two arguments for order. Returns a negative integer, zero, or * a positive integer as the first argument is less than, equal to, * or greater than the second. * The implementor must ensure that * sgn(compare(x, y)) == -sgn(compare(y, x)) for all x and y. * (This implies that compare(x, y) must throw an exception if and * only if compare(y, x) throws an exception.) * The implementor must also ensure that the relation is transitive: * ((compare(x, y)>0) && (compare(y, z)>0)) * implies compare(x, z)>0. Finally, the implementer must ensure that * compare(x, y)==0 implies * that sgn(compare(x, z))==sgn(compare(y, z)) for all z. * It is generally the case, but not strictly required * that (compare(x, y)==0) == (x.equals(y)). * Generally speaking, any comparator that violates this condition should * clearly indicate this fact. **/ public int compare(Object o1, Object o2) { Person p1 = (Person) o1; Person p2 = (Person) o2; int ret = p1.getName().compareTo(p2.getName()); if (ret == 0 && p1.getAge() != p2.getAge()) ret = p1.getAge() < p2.getAge() ? -1 : 1; return ret; } /** * from interface Comparable * Called by Arrays.sort() * @param o * @return */ public int compareTo(Object o) { Person p = (Person) o; int ret = p.getName().compareTo(getName()); if (ret == 0 && p.getAge() != getAge()) ret = p.getAge() < getAge() ? -1 : 1; return ret; } /** * The general contract of hashCode is: * Whenever it is invoked on the same object more than once during an execution * of a Java application, the hashCode method must consistently return the same * integer, provided no information used in equals comparisons on the object is * modified. This integer need not remain consistent from one execution of an * application to another execution of the same application. * ********************** * If two objects are equal according to the equals(Object) method, then calling * the hashCode method on each of the two objects must produce the same integer * result. * ********************** * It is not required that if two objects are unequal according to the * equals(java.lang.Object) method, then calling the hashCode method on each of * the two objects must produce distinct integer results. However, the programmer * should be aware that producing distinct integer results for unequal objects * may improve the performance of hashtables. * x.equals(y) == true then x.hashCode() == y.hashCode() * x.hashCode() == y.hashCode() then x.equals(y) can be true (not required * x.hashCode() != y.hashCode() then x.equals(y) == false * Use algorithm from Effective Java * public int hashCode() { return 1776; } // valid but ineffecient hashCode * do not use transient variables for serialized objects */ public int hashCode() { System.out.println("in Person.hashCode"); int result = 17; // start with some random number result = 37 * result + name.hashCode(); result = 37 * result + age; // for boolean b == true ? 1 : 0; return result; } /** * Create copy of Person * x.clone != x * x.clone().equals(x) * @return copy of this object * @throws java.lang.CloneNotSupportedException */ public Object clone() throws CloneNotSupportedException { Person p = (Person) super.clone(); p.name = name; p.age = age; return p; } public String getName() { return name; } public int getAge() { return age; } public String toString() { return "name=" + name + ",age=" + age; } }