Tuesday 21 March 2017

Comparators & The Collection Algorithms - Java Tutorials

Comparators

Both TreeSet and TreeMap store elements in sorted order. However, it is the comparator that defines precisely what “sorted order” means. By default, these classes store their elements by using what Java refers to as “natural ordering,” which is usually the ordering that you would expect. (A before B, 1 before 2, and so forth.) If you want to order elements a different way, then specify a Comparator object when you construct the set or map. Doing so gives you the ability to govern precisely how elements are stored within sorted collections and maps.

The Comparator interface defines two methods: compare( ) and equals( ). The compare( ) method, shown here, compares two elements for order:

      int compare(Object obj1, Object obj2)

obj1 and obj2 are the objects to be compared. This method returns zero if the objects are equal. It returns a positive value if obj1 is greater than obj2. Otherwise, a negative value is returned. The method can throw a ClassCastException if the types of the objects are not compatible for comparison. By overriding compare( ), you can alter the way that objects are ordered. For example, to sort in reverse order, you can create a comparator that reverses the outcome of a comparison.

The equals( ) method, shown here, tests whether an object equals the invoking comparator:

      boolean equals(Object obj)

obj is the object to be tested for equality. The method returns true if obj and the invoking object are both Comparator objects and use the same ordering. Otherwise, it returns false. Overriding equals( ) is unnecessary, and most simple comparators will not do so.


Using a Comparator

The following is an example that demonstrates the power of a custom comparator. It implements the compare( ) method so that it operates in reverse of normal. Thus, it causes a tree set to be stored in reverse order.

  // Use a custom comparator.
  import java.util.*;

  // A reverse comparator for strings.
  class MyComp implements Comparator {
    public int compare(Object a, Object b) {
      String aStr, bStr;

      aStr = (String) a;
      bStr = (String) b;

      // reverse the comparison
      return bStr.compareTo(aStr);
    }

    // no need to override equals
  }

  class CompDemo {
    public static void main(String args[]) {
      // Create a tree set
      TreeSet ts = new TreeSet(new MyComp());

      // Add elements to the tree set
      ts.add("C");
      ts.add("A");
      ts.add("B");
      ts.add("E");
      ts.add("F");
      ts.add("D");

      // Get an iterator
      Iterator i = ts.iterator();

      // Display elements
      while(i.hasNext()) {
        Object element = i.next();
        System.out.print(element + " ");
      }
      System.out.println();
    }
  }

As the following output shows, the tree is now stored in reverse order:

  F E D C B A

Look closely at the MyComp class, which implements Comparator and overrides compare( ). (As explained earlier, overriding equals( ) is neither necessary nor common.) Inside compare( ), the String method compareTo( ) compares the two strings. However, bStr—not aStr—invokes compareTo( ). This causes the outcome of the comparison to be reversed.

For a more practical example, the following program is an updated version of the TreeMap program shown earlier that stores account balances. In the previous version, the accounts were sorted by name, but the sorting began with the first name. The following program sorts the accounts by last name. To do so, it uses a comparator that compares the last name of each account. This results in the map being sorted by last name.

  // Use a comparator to sort accounts by last name.
  import java.util.*;

  // Compare last whole words in two strings.
  class TComp implements Comparator {
    public int compare(Object a, Object b) {
      int i, j, k;
      String aStr, bStr;

      aStr = (String) a;
      bStr = (String) b;

      // find index of beginning of last name
      i = aStr.lastIndexOf(' ');
      j = bStr.lastIndexOf(' ');

      k = aStr.substring(i).compareTo(bStr.substring(j));
      if(k==0) // last names match, check entire name
        return aStr.compareTo(bStr);
      else
        return k;
    }

    // no need to override equals
  }

  class TreeMapDemo2 {
    public static void main(String args[]) {
      // Create a tree map
      TreeMap tm = new TreeMap(new TComp());

      // Put elements to the map
      tm.put("John Doe", new Double(3434.34));
      tm.put("Tom Smith", new Double(123.22));
      tm.put("Jane Baker", new Double(1378.00));
      tm.put("Todd Hall", new Double(99.22));
      tm.put("Ralph Smith", new Double(-19.08));

      // Get a set of the entries
      Set set = tm.entrySet();

      // Get an iterator
      Iterator itr = set.iterator();

      // Display elements
      while(itr.hasNext()) {
        Map.Entry me = (Map.Entry)itr.next();
        System.out.print(me.getKey() + ": ");
        System.out.println(me.getValue());
      }
      System.out.println();

      // Deposit 1000 into John Doe's account
      double balance = ((Double)tm.get("John Doe")).doubleValue();
      tm.put("John Doe", new Double(balance + 1000));
      System.out.println("John Doe's new balance: " +
                         tm.get("John Doe"));
    }
  }

Here is the output; notice that the accounts are now sorted by last name:

  Jane Baker: 1378.0
  John Doe: 3434.34
  Todd Hall: 99.22
  Ralph Smith: -19.08
  Tom Smith: 123.22
  
  John Doe’s new balance: 4434.34

The comparator class TComp compares two strings that hold first and last names. It does so by first comparing last names. To do this, it finds the index of the last space in each string and then compares the substrings of each element that begin at that point. In cases where last names are equivalent, the first names are then compared. This yields a tree map that is sorted by last name, and within last name by first name. You can see this because Ralph Smith comes before Tom Smith in the output.




The Collection Algorithms

The collections framework defines several algorithms that can be applied to collections and maps. These algorithms are defined as static methods within the Collections class. They are summarized in Table 15-9. Several of the methods can throw a ClassCastException, which occurs when an attempt is made to compare incompatible types, or an UnsupportedOperationException, which occurs when an attempt is made to modify an unmodifiable collection.

Notice that several methods, such as synchronizedList( ) and synchronizedSet( ), are used to obtain synchronized (thread-safe) copies of the various collections. As explained, none of the standard collections implementations are synchronized. You must use the synchronization algorithms to provide synchronization. One other point: iterators to synchronized collections must be used within synchronized blocks.

The set of methods that begins with unmodifiable returns views of the various collections that cannot be modified. These will be useful when you want to grant some process read—but not write—capabilities on a collection.


The Algorithms Defined by Collections

static int binarySearch(List list, Object value, Comparator c):   Searches for value in list ordered according to c. Returns the position of value in list, or −1 if value is not found.

static int binarySearch(List list, Object value):   Searches for value in list. The list must be sorted. Returns the position of value in list, or −1 if value is not found.

static void copy(List list1, List list2):   Copies the elements of list2 to list1.

static Enumeration enumeration(Collection c):  Returns an enumeration over c. (See “The Enumeration Interface,” later in this chapter.)

static void fill(List list, Object obj):   Assigns obj to each element of list.

static int indexOfSubList(List list, List subList):   Searches list for the first occurrence of subList. Returns the index of the first match, or –1 if no match is found. (Added by Java 2, v1.4)

static int lastIndexOfSubList(List list, List subList):   Searches list for the last occurrence of subList. Returns the index of the last match, or –1 if no match is found. (Added by Java 2, v1.4)

static ArrayList list(Enumeration enum):   Returns an ArrayList that contains the elements of enum. (Added by Java 2, v1.4)

static Object max(Collection c, Comparator comp):   Returns the maximum element in c as determined by comp.

static Object max(Collection c):  Returns the maximum element in c as determined by natural ordering. The collection need not be sorted.

static Object min(Collection c, Comparator comp):   Returns the minimum element in c as determined by comp. The collection need not be sorted.

static Object min(Collection c):   Returns the minimum element in c as determined by natural ordering.

static List nCopies(int num, Object obj):   Returns num copies of obj contained in an immutable list. num must be greater than or equal to zero.

static boolean replaceAll(List list, Object old, Object new):   Replaces all occurrences of old with new in list. Returns true if at least one replacement occurred. Returns false, otherwise. (Added by Java 2, v1.4)

static void reverse(List list):   Reverses the sequence in list.

static Comparator reverseOrder( ):   Returns a reverse comparator (a comparator that reverses the outcome of a comparison between two elements).

static void rotate(List list, int n):   Rotates list by n places to the right. To rotate left, use a negative value for n. (Added by Java 2, v1.4)

static void shuffle(List list, Random r):   Shuffles (i.e., randomizes) the elements in list by using r as a source of random numbers.

static void shuffle(List list):   Shuffles (i.e., randomizes) the elements in list.

static Set singleton(Object obj):   Returns obj as an immutable set. This is an easy way to convert a single object into a set.

static List singletonList(Object obj):   Returns obj as an immutable list. This is an easy way to convert a single object into a list. (Added by Java 2, v1.3)

static Map singletonMap(Object k, Object v):   Returns the key/value pair k/v as an immutable map. This is an easy way to convert a single key/value pair into a map. (Added by Java 2, v1.3)

static void sort(List list, Comparator comp):   Sorts the elements of list as determined by comp.

static void sort(List list):   Sorts the elements of list as determined by their natural ordering.

static void swap(List list, int idx1, int idx2):   Exchanges the elements in list at the indices specified by idx1 and idx2. (Added by Java 2, v1.4)

static Collection synchronizedCollection(Collection c):   Returns a thread-safe collection backed by c.

static List synchronizedList(List list):   Returns a thread-safe list backed by list.

static Map synchronizedMap(Map m):   Returns a thread-safe map backed by m.

static Set synchronizedSet(Set s):   Returns a thread-safe set backed by s.

static SortedMap synchronizedSortedMap(SortedMap sm):   Returns a thread-safe sorted set backed by sm.

static SortedSet synchronizedSortedSet(SortedSet ss):   Returns a thread-safe set backed by ss.

static Collection unmodifiableCollection(Collection c):   Returns an unmodifiable collection backed by c.

static List unmodifiableList(List list):   Returns an unmodifiable list backed by list.

static Map unmodifiableMap(Map m):   Returns an unmodifiable map backed by m.

static Set unmodifiableSet(Set s):   Returns an unmodifiable set backed by s.

static SortedMap unmodifiableSortedMap(SortedMap sm):   Returns an unmodifiable sorted map backed by sm.

static SortedSet unmodifiableSortedSet(SortedSet ss):   Returns an unmodifiable sorted set backed by ss.


Collections defines three static variables: EMPTY_SET, EMPTY_LIST, and EMPTY_MAP. All are immutable. EMPTY_MAP was added by Java 2, version 1.3.

The following program demonstrates some of the algorithms. It creates and initializes a linked list. The reverseOrder( ) method returns a Comparator that reverses the comparison of Integer objects. The list elements are sorted according to this comparator and then are displayed. Next, the list is randomized by calling shuffle( ), and then its minimum and maximum values are displayed.

  // Demonstrate various algorithms.
  import java.util.*;

  class AlgorithmsDemo {
    public static void main(String args[]) {

      // Create and initialize linked list
      LinkedList ll = new LinkedList();
      ll.add(new Integer(-8));
      ll.add(new Integer(20));
      ll.add(new Integer(-20));
      ll.add(new Integer(8));

      // Create a reverse order comparator
      Comparator r = Collections.reverseOrder();

      // Sort list by using the comparator
      Collections.sort(ll, r);

      // Get iterator
      Iterator li = ll.iterator();

      System.out.print("List sorted in reverse: ");
      while(li.hasNext())
        System.out.print(li.next() + " ");
      System.out.println();

      Collections.shuffle(ll);

      // display randomized list
      li = ll.iterator();
      System.out.print("List shuffled: ");
      while(li.hasNext())
        System.out.print(li.next() + " ");
      System.out.println();

      System.out.println("Minimum: " + Collections.min(ll));
      System.out.println("Maximum: " + Collections.max(ll));
    }
  }

Output from this program is shown here:

  List sorted in reverse: 20 8 -8 -20
  List shuffled: 20 -20 8 -8
  Minimum: -20
  Maximum: 20

Notice that min( ) and max( ) operate on the list after it has been shuffled. Neither requires a sorted list for its operation.

Working with Maps - Java Tutorials

A map is an object that stores associations between keys and values, or key/value pairs. Given a key, you can find its value. Both keys and values are objects. The keys must be unique, but the values may be duplicated. Some maps can accept a null key and null values, others cannot.


The Map Interfaces

Because the map interfaces define the character and nature of maps, this discussion of maps begins with them. The following interfaces support maps:

Map:  Maps unique keys to values.

Map.Entry:  Describes an element (a key/value pair) in a map. This is an inner class of Map.

SortedMap:  Extends Map so that the keys are maintained in ascending order.

Each interface is examined next, in turn.


The Map Interface

The Map interface maps unique keys to values. A key is an object that you use to retrieve a value at a later date. Given a key and a value, you can store the value in a Map object. After the value is stored, you can retrieve it by using its key. Several methods throw a NoSuchElementException when no items exist in the invoking map. A ClassCastException is thrown when an object is incompatible with the elements in a map. A NullPointerException is thrown if an attempt is made to use a null object and null is not allowed in the map. An UnsupportedOperationException is thrown when an attempt is made to change an unmodifiable map.


The Methods Defined by Map

void clear( ):  Removes all key/value pairs from the invoking map.

boolean containsKey(Object k):  Returns true if the invoking map contains k as a key. Otherwise, returns false.

boolean containsValue(Object v):  Returns true if the map contains v as a value. Otherwise, returns false.

Set entrySet( ):  Returns a Set that contains the entries in the map. The set contains objects of type Map.Entry. This method provides a set-view of the invoking map.

boolean equals(Object obj):  Returns true if obj is a Map and contains the same entries. Otherwise, returns false.

Object get(Object k):  Returns the value associated with the key k.

int hashCode( ):  Returns the hash code for the invoking map.

boolean isEmpty( ):  Returns true if the invoking map is empty. Otherwise, returns false.

Set keySet( ):  Returns a Set that contains the keys in the invoking map. This method provides a set-view of the keys in the invoking map.

Object put(Object k, Object v):  Puts an entry in the invoking map, overwriting any previous value associated with the key. The key and value are k and v, respectively. Returns null if the key did not already exist. Otherwise, the previous value linked to the key is returned.

void putAll(Map m):  Puts all the entries from m into this map.

Object remove(Object k):  Removes the entry whose key equals k.

int size( ):  Returns the number of key/value pairs in the map.

Collection values( ):  Returns a collection containing the values in the map. This method provides a collection-view of the values in the map.


Maps revolve around two basic operations: get( ) and put( ). To put a value into a map, use put( ), specifying the key and the value. To obtain a value, call get( ), passing the key as an argument. The value is returned.

As mentioned earlier, maps are not collections because they do not implement the Collection interface, but you can obtain a collection-view of a map. To do this, you can use the entrySet( ) method. It returns a Set that contains the elements in the map. To obtain a collection-view of the keys, use keySet( ). To get a collection-view of the values, use values( ). Collection-views are the means by which maps are integrated into the collections framework.


The SortedMap Interface

The SortedMap interface extends Map. It ensures that the entries are maintained in ascending key order. Several methods throw a NoSuchElementException when no items are in the invoking map. A ClassCastException is thrown when an object is incompatible with the elements in a map. A NullPointerException is thrown if an attempt is made to use a null object when null is not allowed in the map.

Sorted maps allow very efficient manipulations of submaps (in other words, a subset of a map). To obtain a submap, use headMap( ), tailMap( ), or subMap( ). To get the first key in the set, call firstKey( ). To get the last key, use lastKey( ).


The Methods Defined by SortedMap

Comparator comparator( ):  Returns the invoking sorted map’s comparator. If the natural ordering is used for the invoking map, null is returned.

Object firstKey( ):  Returns the first key in the invoking map.

SortedMap headMap(Object end):  Returns a sorted map for those map entries with keys that are less than end.

Object lastKey( ):  Returns the last key in the invoking map.

SortedMap subMap(Object start, Object end):  Returns a map containing those entries with keys that are greater than or equal to start and less than end.

SortedMap tailMap(Object start):  Returns a map containing those entries with keys that are greater
than or equal to start.


The Map.Entry Interface

The Map.Entry interface enables you to work with a map entry. Recall that the entrySet( ) method declared by the Map interface returns a Set containing the map entries. Each of these set elements is a Map.Entry object. Table 15-8 summarizes the methods declared by this interface.


The Map Classes

Several classes provide implementations of the map interfaces. The classes that can be used for maps are summarized here:

AbstractMap:  Implements most of the Map interface.

HashMap:  Extends AbstractMap to use a hash table.

TreeMap:  Extends AbstractMap to use a tree.

WeakHashMap:  Extends AbstractMap to use a hash table with weak keys.

LinkedHashMap:  Extends HashMap to allow insertion-order iterations.

IdentityHashMap:  Extends AbstractMap and uses reference equality when comparing documents.


The Methods Defined by Map.Entry

boolean equals(Object obj):  Returns true if obj is a Map.Entry whose key and value are equal to that of the invoking object.

Object getKey( ):  Returns the key for this map entry.

Object getValue( ):  Returns the value for this map entry.

int hashCode( ):  Returns the hash code for this map entry.

Object setValue(Object v):  Sets the value for this map entry to v. A ClassCastException is thrown if v is not the correct type for the map. An IllegalArgumentException is thrown if there is a problem with v. A NullPointerException is thrown if v is null and the map does not permit null keys. An UnsupportedOperationException is thrown if the map cannot be changed.


Notice that AbstractMap is a superclass for all concrete map implementations. WeakHashMap implements a map that uses “weak keys,” which allows an element in a map to be garbage-collected when its key is unused. This class is not discussed further here. The others are described next.


The HashMap Class

The HashMap class uses a hash table to implement the Map interface. This allows the execution time of basic operations, such as get( ) and put( ), to remain constant even for large sets.

The following constructors are defined:

      HashMap( )
      HashMap(Map m)
      HashMap(int capacity)
      HashMap(int capacity, float fillRatio)

The first form constructs a default hash map. The second form initializes the hash map by using the elements of m. The third form initializes the capacity of the hash map to capacity. The fourth form initializes both the capacity and fill ratio of the hash map by using its arguments. The meaning of capacity and fill ratio is the same as for HashSet, described earlier.

HashMap implements Map and extends AbstractMap. It does not add any methods of its own. You should note that a hash map does not guarantee the order of its elements. Therefore, the order in which elements are added to a hash map is not necessarily the order in which they are read by an iterator.

The following program illustrates HashMap. It maps names to account balances. Notice how a set-view is obtained and used.

  import java.util.*;

  class HashMapDemo {
    public static void main(String args[]) {

      // Create a hash map
      HashMap hm = new HashMap();

      // Put elements to the map
      hm.put("John Doe", new Double(3434.34));
      hm.put("Tom Smith", new Double(123.22));
      hm.put("Jane Baker", new Double(1378.00));
      hm.put("Todd Hall", new Double(99.22));
      hm.put("Ralph Smith", new Double(-19.08));

      // Get a set of the entries
      Set set = hm.entrySet();

      // Get an iterator
      Iterator i = set.iterator();

      // Display elements
      while(i.hasNext()) {
        Map.Entry me = (Map.Entry)i.next();
        System.out.print(me.getKey() + ": ");
        System.out.println(me.getValue());
      }
      System.out.println();

      // Deposit 1000 into John Doe's account
      double balance = ((Double)hm.get("John Doe")).doubleValue();
      hm.put("John Doe", new Double(balance + 1000));
      System.out.println("John Doe's new balance: " +
        hm.get("John Doe"));
    }
  }

Output from this program is shown here (the precise order may vary).

  Todd Hall: 99.22
  Ralph Smith: -19.08
  John Doe: 3434.34
  Jane Baker: 1378.0
  Tom Smith: 123.22

  John Doe’s current balance: 4434.34

The program begins by creating a hash map and then adds the mapping of names to balances. Next, the contents of the map are displayed by using a set-view, obtained by calling entrySet( ). The keys and values are displayed by calling the getKey( ) and getValue( ) methods that are defined by Map.Entry. Pay close attention to how the deposit is made into John Doe’s account. The put( ) method automatically replaces any preexisting value that is associated with the specified key with the new value. Thus, after John Doe’s account is updated, the hash map will still contain just one “John Doe” account.


The TreeMap Class

The TreeMap class implements the Map interface by using a tree. A TreeMap provides an efficient means of storing key/value pairs in sorted order, and allows rapid retrieval. You should note that, unlike a hash map, a tree map guarantees that its elements will be sorted in ascending key order.

The following TreeMap constructors are defined:

      TreeMap( )
      TreeMap(Comparator comp)
      TreeMap(Map m)
      TreeMap(SortedMap sm)

The first form constructs an empty tree map that will be sorted by using the natural order of its keys. The second form constructs an empty tree-based map that will be sorted by using the Comparator comp. (Comparators are discussed later in this chapter.) The third form initializes a tree map with the entries from m, which will be sorted by using the natural order of the keys. The fourth form initializes a tree map with the entries from sm, which will be sorted in the same order as sm.

TreeMap implements SortedMap and extends AbstractMap. It does not define any additional methods of its own.

The following program reworks the preceding example so that it uses TreeMap:

  import java.util.*;

  class TreeMapDemo {
    public static void main(String args[]) {

      // Create a tree map
      TreeMap tm = new TreeMap();

      // Put elements to the map
      tm.put("John Doe", new Double(3434.34));
      tm.put("Tom Smith", new Double(123.22));
      tm.put("Jane Baker", new Double(1378.00));
      tm.put("Todd Hall", new Double(99.22));
      tm.put("Ralph Smith", new Double(-19.08));

      // Get a set of the entries
      Set set = tm.entrySet();

      // Get an iterator
      Iterator i = set.iterator();

      // Display elements
      while(i.hasNext()) {
        Map.Entry me = (Map.Entry)i.next();
        System.out.print(me.getKey() + ": ");
        System.out.println(me.getValue());
      }
      System.out.println();

      // Deposit 1000 into John Doe's account
      double balance = ((Double)tm.get("John Doe")).doubleValue();
      tm.put("John Doe", new Double(balance + 1000));
      System.out.println("John Doe's new balance: " +
        tm.get("John Doe"));
    }
  }

The following is the output from this program:

  Jane Baker: 1378.0
  John Doe: 3434.34
  Ralph Smith: -19.08
  Todd Hall: 99.22
  Tom Smith: 123.22
  
  John Doe’s current balance: 4434.34

Notice that TreeMap sorts the keys. However, in this case, they are sorted by first name instead of last name. You can alter this behavior by specifying a comparator when the map is created, as described shortly.


The LinkedHashMap Class

Java 2, version 1.4 adds the LinkedHashMap class. This class extends HashMap.
LinkedHashMap maintains a linked list of the entries in the map, in the order in which
they were inserted. This allows insertion-order iteration over the map. That is, when
iterating a LinkedHashMap, the elements will be returned in the order in which they
were inserted. You can also create a LinkedHashMap that returns its elements in the
order in which they were last accessed.

LinkedHashMap defines the following constructors.

      LinkedHashMap( )
      LinkedHashMap(Map m)
      LinkedHashMap(int capacity)
      LinkedHashMap(int capacity, float fillRatio)
      LinkedHashMap(int capacity, float fillRatio, boolean Order)

The first form constructs a default LinkedHashMap. The second form initializes the LinkedHashMap with the elements from m. The third form initializes the capacity. The fourth form initializes both capacity and fill ratio. The meaning of capacity and fill ratio are the same as for HashMap. The last form allows you to specify whether the elements will be stored in the linked list by insertion order, or by order of last access. If Order is true, then access order is used. If Order is false, then insertion order is used. LinkedHashMap adds only one method to those defined by HashMap. This method is removeEldestEntry( ) and it is shown here.

      protected boolean removeEldestEntry(Map.Entry e)

This method is called by put( ) and putAll( ). The oldest entry is passed in e. By default, this method returns false and does nothing. However, if you override this method, then you can have the LinkedHashMap remove the oldest entry in the map. To do this, have your override return true. To keep the oldest entry, return false.


The IdentityHashMap Class

Java 2, version 1.4 adds the IdentityHashMap class. This class implements AbstractMap. It is similar to HashMap except that it uses reference equality when comparing elements. The Java 2 documentation explicitly states that IdentityHashMap is not for general use.

Accessing a Collection via an Iterator & Storing User-Defined Classes in Collections - Java Tutorials

Accessing a Collection via an Iterator

Often, you will want to cycle through the elements in a collection. For example, you might want to display each element. By far, the easiest way to do this is to employ an iterator, an object that implements either the Iterator or the ListIterator interface. Iterator enables you to cycle through a collection, obtaining or removing elements. ListIterator extends Iterator to allow bidirectional traversal of a list, and the modification of elements. 


Using an Iterator

Before you can access a collection through an iterator, you must obtain one. Each of the collection classes provides an iterator( ) method that returns an iterator to the start of the collection. By using this iterator object, you can access each element in the collection, one element at a time. In general, to use an iterator to cycle through the contents of a collection, follow these steps:
  1. Obtain an iterator to the start of the collection by calling the collection’s iterator( ) method.
  2. Set up a loop that makes a call to hasNext( ). Have the loop iterate as long as hasNext( ) returns true.
  3. Within the loop, obtain each element by calling next( ).


The Methods Declared by Iterator

boolean hasNext( ):  Returns true if there are more elements. Otherwise, returns false.

Object next( ):  Returns the next element. Throws NoSuchElementException if there is not a next element.

void remove( ):  Removes the current element. Throws IllegalStateException if an attempt is made to call remove( ) that is not preceded by a call to next( ).


The Methods Declared by ListIterator

void add(Object obj):  Inserts obj into the list in front of the element that will be returned by the next call to next( ).

boolean hasNext( ):  Returns true if there is a next element. Otherwise, returns false.

boolean hasPrevious( ):  Returns true if there is a previous element. Otherwise, returns false.

Object next( ):  Returns the next element. A NoSuchElementException is thrown if there is not a next element.

int nextIndex( ):  Returns the index of the next element. If there is not a next element, returns the size of the list.

Object previous( ):  Returns the previous element. A NoSuchElementException is thrown if there is not a previous element.

int previousIndex( ):  Returns the index of the previous element. If there is not a previous element, returns −1.

void remove( ):  Removes the current element from the list. An IllegalStateException is thrown if remove( ) is called before next( ) or previous( ) is invoked.

void set(Object obj):  Assigns obj to the current element. This is the element last returned by a call to either next( ) or previous( ).


For collections that implement List, you can also obtain an iterator by calling ListIterator. As explained, a list iterator gives you the ability to access the collection in either the forward or backward direction and lets you modify an element. Otherwise, ListIterator is used just like Iterator.

Here is an example that implements these steps, demonstrating both Iterator and ListIterator. It uses an ArrayList object, but the general principles apply to any type of collection. Of course, ListIterator is available only to those collections that implement the List interface.

  // Demonstrate iterators.
  import java.util.*;

  class IteratorDemo {
    public static void main(String args[]) {
      // create an array list
      ArrayList al = new ArrayList();

      // add elements to the array list
      al.add("C");
      al.add("A");
      al.add("E");
      al.add("B");
      al.add("D");
      al.add("F");

      // use iterator to display contents of al
      System.out.print("Original contents of al: ");
      Iterator itr = al.iterator();
      while(itr.hasNext()) {
        Object element = itr.next();
        System.out.print(element + " ");
      }
      System.out.println();

      // modify objects being iterated
      ListIterator litr = al.listIterator();
      while(litr.hasNext()) {
        Object element = litr.next();
        litr.set(element + "+");
      }
      
      System.out.print("Modified contents of al: ");
      itr = al.iterator();
      while(itr.hasNext()) {
        Object element = itr.next();
        System.out.print(element + " ");
      }
      System.out.println();

      // now, display the list backwards
      System.out.print("Modified list backwards: ");
      while(litr.hasPrevious()) {
        Object element = litr.previous();
        System.out.print(element + " ");
      }
      System.out.println();
    }
  }

The output is shown here:

  Original contents of al: C A E B D F
  Modified contents of al: C+ A+ E+ B+ D+ F+
  Modified list backwards: F+ D+ B+ E+ A+ C+

Pay special attention to how the list is displayed in reverse. After the list is modified, litr points to the end of the list. (Remember, litr.hasNext( ) returns false when the end of the list has been reached.) To traverse the list in reverse, the program continues to use litr, but this time it checks to see whether it has a previous element. As long as it does, that element is obtained and displayed.




Storing User-Defined Classes in Collections

For the sake of simplicity, the foregoing examples have stored built-in objects, such as String or Integer, in a collection. Of course, collections are not limited to the storage of built-in objects. Quite the contrary. The power of collections is that they can store any type of object, including objects of classes that you create. For example, consider the following example that uses a LinkedList to store mailing addresses:

  // A simple mailing list example.
  import java.util.*;

  class Address {
    private String name;
    private String street;
    private String city;
    private String state;
    private String code;

    Address(String n, String s, String c,
            String st, String cd) {
      name = n;
      street = s;
      city = c;
      state = st;
      code = cd;
    }

    public String toString() {
      return name + "\n" + street + "\n" +
             city + " " + state + " " + code;
    }
  }

  class MailList {
    public static void main(String args[]) {
      LinkedList ml = new LinkedList();

      // add elements to the linked list
      ml.add(new Address("J.W. West", "11 Oak Ave",
                         "Urbana", "IL", "61801"));
      ml.add(new Address("Ralph Baker", "1142 Maple Lane",
                         "Mahomet", "IL", "61853"));
      ml.add(new Address("Tom Carlton", "867 Elm St",
                         "Champaign", "IL", "61820"));

      Iterator itr = ml.iterator();
      while(itr.hasNext()) {
        Object element = itr.next();
        System.out.println(element + "\n");
      }
      System.out.println();
    }
  }

The output from the program is shown here:

  J.W. West
  11 Oak Ave
  Urbana IL 61801
  Ralph Baker
  1142 Maple Lane
  Mahomet IL 61853
  Tom Carlton
  867 Elm St
  Champaign IL 61820

Aside from storing a user-defined class in a collection, another important thing to notice about the preceding program is that it is quite short. When you consider that it sets up a linked list that can store, retrieve, and process mailing addresses in about 50 lines of code, the power of the collections framework begins to become apparent. As most readers know, if all of this functionality had to be coded manually, the program would be several times longer. Collections offer off-the-shelf solutions to a wide variety of programming problems. You should use them whenever the situation presents itself.


The RandomAccess Interface

Java 2, version 1.4 adds the RandomAccess interface. This interface contains no members. However, by implementing this interface, a collection signals that it supports efficient random access to its elements. Although a collection might support random access, it might not do so efficiently. By checking for the RandomAccess interface, client code can determine at run time whether a collection is suitable for certain types of random access operations—especially as they apply to large collections. (You can use instanceof to determine if a class implements an interface.) RandomAccess is implemented by ArrayList and by the legacy Vector class.