Published: July 2, 2001
By Richard G. Baldwin
Java Programming, Lecture Notes # 1358
The purpose of this miniseries is to help you learn the essential features of Object-Oriented data structures in Java using the Collections Framework.
Viewing tip
You may find it useful to open another copy of this lesson in a separate browser window. That will make it easier for you to scroll back and forth among the different listings while you are reading about them.
Supplementary material
I recommend that you also study the other lessons in my extensive collection of online Java tutorials. You will find those lessons published at Gamelan.com. However, as of the date of this writing, Gamelan doesn't maintain a consolidated index of my Java tutorial lessons, and sometimes they are difficult to locate there. You will find a consolidated index at Baldwin's Java Programming Tutorials.
The index on my site provides links to the lessons at Gamelan.com.
The Java Collections Framework defines six core interfaces, in two distinct trees. You will learn the names and the inheritance structure of those interfaces. You will also learn about their purpose. You will see how the interfaces declare polymorphic methods that apply to implementations of the interfaces, and you will learn about the optional methods of the Collection interface.
Let’s begin this lesson with a little quiz. Take a look at the program shown in Listing 1 and see if you can answer the following question.
What output does the program in Listing 1 produce?
import java.util.TreeSet; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class Ap401{ public static void main( String args[]){ new Worker().doIt(); }//end main() }//end class Ap401 class Worker{ public void doIt(){ Collection ref = new TreeSet(); Populator.fillIt(ref); Iterator iter = ref.iterator(); while(iter.hasNext()){ System.out.print(iter.next()); }//end while loop System.out.print(" "); ref = new ArrayList(); Populator.fillIt(ref); iter = ref.iterator(); while(iter.hasNext()){ System.out.print(iter.next()); }//end while loop System.out.println(); }//end doIt() }// end class Worker class Populator{ public static void fillIt( Collection ref){ ref.add(new Integer(4)); ref.add(new Integer(4)); ref.add(new Integer(3)); ref.add(new Integer(2)); ref.add(new Integer(1)); }//end fillIt() }//end class populator Listing 1 |
If you selected the following answer, then you are correct.
E. 1234 44321
The program in Listing 1 illustrates the basic purpose of the core collection interfaces in the Java Collections Framework. That purpose is to allow collections to be manipulated without regard for how the collections are implemented.
Multiple list implementations
For example, there is more than one way to implement a list. Two common ways involve arrays and linked structures. If two lists are implemented in different ways, but both satisfy the requirements of the core collection interfaces, they can each be manipulated the same way regardless of the details of their implementation.
TreeSet and ArrayList
A collection of type TreeSet and a collection of type ArrayList are instantiated in the program in Listing 1. Each of the collections is viewed as being of the interface type Collection. A method named add() is used to populate each collection with the same values.
Behavior is different but appropriate
The behavior of the add() method is appropriate, and different in each of the two cases, with the final contents of each collection being determined by the respective behavior of the add() method for that type of collection.
The fillIt() method
The code in the fragment shown in Listing 2 defines a class method named fillIt() of the class named Populator. This is a class of my own design intended solely to illustrate the primary point of this program.
The method named fillIt() receives an incoming reference to a
collection object as type Collection. The method invokes the
add()
method on the incoming reference five times in succession to add five elements
to the collection. These elements are added without regard for the
actual type or underlying implementation of the collection (in fact,
as the method is written, it has no way of knowing the underlying implementation).
class Populator{ public static void fillIt( Collection ref){ ref.add(new Integer(4)); ref.add(new Integer(4)); ref.add(new Integer(3)); ref.add(new Integer(2)); ref.add(new Integer(1)); }//end fillIt() }//end class populator Listing 2 |
The fillIt() method will be used to populate two collections of different types with the same data.
Create and populate a TreeSet object
Consider the code fragment shown in Listing 3.
Collection ref = new TreeSet(); Populator.fillIt(ref); Iterator iter = ref.iterator(); while(iter.hasNext()){ System.out.print(iter.next()); }//end while loop Listing 3 |
The code in Listing 3 instantiates an object of type TreeSet, and passes that object's reference to the fillIt() method as type Collection. As described above, the fillIt() method adds five elements to the collection, in random order with two of the elements being duplicates.
Display the collection's contents
Then the code in Listing 3 gets an Iterator object on the collection and uses the iterator to display the contents of the collection.
TreeSet object is type SortedSet
The TreeSet class implements one of the core collection interfaces named SortedSet. SortedSet is a sub interface of Set. One of the characteristics of a Set object is that it doesn't allow duplicate elements. One of the characteristics of a SortedSet object is that it maintains its elements in ascending order. Since the TreeSet class implements both of these interfaces, it is both a Set and a SortedSet, and exhibits the characteristics of both interfaces.
Because the underlying structure of the TreeSet class doesn't allow duplicates, and the underlying structure maintains its elements in ascending order, the code in Listing 3 produces the following text on the screen:
1234
Create and populate an ArrayList object
Now consider the code fragment shown in Listing 4.
ref = new ArrayList(); Populator.fillIt(ref); iter = ref.iterator(); while(iter.hasNext()){ System.out.print(iter.next()); }//end while loop Listing 4 |
The code in Listing 4 instantiates a new collection of type ArrayList, and passes that object's reference to the same fillIt() method, again as type collection.
The code in the fillIt() method adds five elements having the same values as before to the collection. The added elements are references to Integer objects encapsulating the same values as were earlier added to the TreeSet collection. Although they are physically different objects, the result is that essentially the same data is added to both collections.
Display the collection's contents
Then, as before, the code in Listing 4 gets an iterator and uses it to access and display the contents of the ArrayList collection.
The ArrayList class implements the List interface, which does not prohibit duplicate elements, and does not maintain its elements in sorted order. Therefore, in this case, the following text was displayed:
44321
All five element values are displayed, including the duplicate, in the order in which they were added to the list.
The important point
The important point is that although the fillIt() method invokes the same method name (add()) on each of the collection objects, the behavior of that method is different in each case. In both cases, the behavior is appropriate for the underlying data structure. Furthermore, the underlying data structure isn't even known to the fillIt() method.
No duplicate elements in ascending order
In the first case, where the underlying data structure was a TreeSet object (type SortedSet), the duplicate element was eliminated, and the elements were stored so as to be accessible in ascending order.
Duplicates allowed with no sorting
In the second case, where the underlying data structure was an ArrayList object (type List), all five elements, including the duplicate element were stored in the collection. Furthermore, they were stored and later retrieved in the same order in which they were added.
Structure of core the interfaces
Interestingly, the core collection interfaces in the Java Collections Framework do not all extend from a common root interface.
Rather, the inheritance structure of the core interfaces is shown below. Indentation is used to indicate the superinterface-subinterface relationship among the interfaces.
As you can see, that there is no common root interface. Rather, there are two distinct trees, one rooted by Collection and the other rooted by Map. According to The Java Tutorial from Sun, "a Map is not a true Collection." I will have more to say about this in a future lesson.
Some operations are optional
Every class that implements an interface in the tree rooted in Collection is not required to support all of the modification methods (operations) declared in the Collection interface.
Rather, the modification methods (operations) in the Collection interface are designated optional. (See the list of optional modification methods for the Collection interface below.)
According to the contract for the Collections Framework, if a given implementation doesn't support a specific modification method, it must throw an UnsupportedOperationException. The author of the implementation is responsible for providing documentation that identifies the optional operations that the implementation does and does not support. (I have read that this approach has been the source of some controversy within the Java community, but I haven't pursued that controversy in any detail.)
Support for optional operations
This should not be an issue unless you are either defining your own implementation, or using an implementation defined by someone other than the programmers at Sun. As of JDK 1.3, all of the general-purpose implementations from Sun support all of the optional operations.
Optional Collection operations
The following list shows the optional operations in the Collection interface as of JDK 1.3. Each of these methods has the ability to modify the contents of the collection.
As of JDK 1.3, the following list shows the optional operations in the Map interface. Each of these methods has the ability to modify the contents of the map.
In both cases, the interface declares numerous other methods that are not optional. Generally, the non-optional methods don't have the ability to modify the collection. For example, the get() method of the Map interface is not optional. Although the get() method receives an incoming key and returns the value to which the key maps, the method doesn't have the ability to modify the contents of the collection.
The basic purpose of the core interfaces is to make it possible for collections to be manipulated without regard for how they are implemented, so long as the implementation satisfies the contracts of the interfaces.
When the same method name is invoked on references to collections of different types, the behavior of the method is likely to be different for each collection. However, in each case, that behavior will be appropriate for the type of collection object on which the method is invoked. This is polymorphic behavior.
Six of the methods declared in the Collection interface are optional insofar as being supported by implementing classes. The optional methods all have the ability to modify the contents of the collection. Those implementing classes that don't support an optional method must throw an UnsupportedOperationException if that method is invoked on an object of the class.
Many methods declared in the Collection interface are not optional. Generally, the non-optional methods don't have the ability to modify the collection.
All of the general-purpose implementation classes of the Collection interface in JDK 1.3 support all of the optional methods.
Copyright 2000, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.
Richard has participated in numerous consulting projects involving Java, XML, or a combination of the two. He frequently provides onsite Java and/or XML training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Java Programming Tutorials, which has gained a worldwide following among experienced and aspiring Java programmers. He has also published articles on Java Programming in Java Pro magazine.
Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.
-end-