| Home | JSP | EJB | JDBC | Java Servlets | WAP | Free JSP Hosting | Spring Framework | Web Services | BioInformatics | Java Server Faces | Jboss 3.0 tutorial | Hibernate 3.0 | XML | ||||
|
||||
|
|
||||
| Tutorial Categories: Ajax
| Articles
| JSP
| Bioinformatics
| Database
| Free
Books |
Hibernate
| J2EE
| J2ME
| Java
| JavaScript
| JDBC
| JMS
| Linux
| MS
Technology |
PHP
| RMI
| Web-Services
| Servlets
| Struts
| UML
|
|
Collections and MapsJava's generic data structures can be divided into two categories: collections and maps. A collection is more or less what it sound like: a collection of objects. A map associates objects in one set with objects in another set in the way that a dictionary associates definitions with words or a phone book associates phone numbers with names. A map is similar to what I called an "association list" in Section 8.4. There are two types of collections: lists and sets. A list is a collection in which the objects are arranged in a linear sequence. A list has a first item, a second item, and so on. For any item in the list, except the last, there is an item that directly follows it. A set is a collection in which no object can appear more than once. Note that the terms "collection," "list," "set," and "map" tell you nothing about how the data is stored. A list could be represented as an array, as a linked list, or, for that matter, as a map that associates the elements of the list to the numbers 0, 1, 2, .... In fact, these terms are represented in Java not by classes but by interfaces. The interfaces Collection, List, Set, and Map specify the basic operations on data structures of these types, but do not specify how the data structures are to be represented or how the operations are to be implemented. That will be specified in the classes that implement the interfaces. Even when you use these classes, you might not know what the implementation is unless you go look at the source code. Java's generic data structures are abstract data types. They are defined by the operations that can be performed on them, not by the physical layout of the data in the computer. We will look at list and set classes in Section 2 and map classes in Section 3. But before we do that, we'll look briefly at some of the general operations that are available for all collections. Generic Algorithms and IteratorsThe Collection interface includes methods for performing some basic operations on collections of objects. Since "collection" is a very general concept, operations that can be applied to all collections are also very general. They are generic operations in the sense that they can be applied to various types of collections containing various types of objects. Suppose that coll is any object that implements the Collection interface. Here are some of the operations that are defined:
Since these methods are part of the Collection interface, they must be defined for every object that implements that interface. There is a problem with this, however. For example, the size of some kinds of Collection cannot be changed after they are created. Methods that add or remove objects don't make sense for these collections. While it is still legal to call the methods, an exception will be thrown when the call is evaluated at run time. The type of exception is UnsupportedOperationException. There is also the question of efficiency. Even when an operation is defined for several types of collections, it might not be equally efficient in all cases. Even a method as simple as size() can vary greatly in efficiency. For some collections, computing the size() might involve counting the items in the collection. The number of steps in this process is equal to the number of items. Other collections might have instance variables to keep track of the size, so evaluating size() just means returning the value of a variable. In this case, the computation takes only one step, no matter how many items there are. When working with collections, it's good to have some idea of how efficient operations are and to choose a collection for which the operations you need can be implemented most efficiently. We'll see specific examples of this in the next two sections. The Collection interface defines a few basic generic algorithms, but suppose you want to write your own generic algorithms. Suppose, for example, you want to do something as simple as printing out every item in a collection. To do this in a generic way, you need some way of going through an arbitrary collection, accessing each item in turn. We have seen how to do this for specific data structures: For an array, you can use a for loop to iterate through all the array indices. For a linked list, you can use a while loop in which you advance a pointer along the list. For a binary tree, you can use a recursive subroutine to do an infix traversal. Collections can be represented in any of these forms and many others besides. With such a variety of traversal mechanisms, how can we hope to come up with a single generic method that will work for collections that are stored in wildly different forms? This problem is solved by iterators. An iterator is an object that can be used to traverse a collection. Different types of collections have different types of iterators, but all iterators are used in the same way. An algorithm that uses an iterator to traverse a collection is generic, because the same technique can be applied to any type of collection. Iterators can seem rather strange to someone who is encountering generic programming for the first time, but you should understand that they solve a difficult problem in an elegant way. The Collection interface defines a method that can be used to obtain an iterator for any collection. If coll is a collection, then coll.iterator() returns an iterator that can be used to traverse the collection. You should think of the iterator as a kind of generalized pointer that starts at the beginning of the collection and can move along the collection from one item to the next. Iterators are defined by an interface named Iterator. This interface defines just three methods. If iter refers to an Iterator, then:
Using iterators, we can write code for printing all the items in any collection. Suppose that coll is of type Collection. Then we can say: Iterator iter = coll.iterator();
while ( iter.hasNext() ) {
Object item = iter.next();
System.out.println(item);
}
The same general form will work for other types of processing. For example, here is a subroutine that will remove all null values from any collection (as long as that collection supports removal of values): void removeNullValues( Collection coll ) {
Iterator iter = coll.iterator():
while ( iter.hasNext() ) {
Object item = iter.next();
if (item == null)
iter.remove();
}
}
Collections can hold objects of any type, so the return value of iter.next() is Object. Now, there's not very much you can do with a general Object. In practical situations, a collection is used to hold objects belonging to some more specific class, and objects from the collection are type-cast to that class before they are used. Suppose, for example, that we are working with Shapes, where Shape is a class that represents geometric figures. Suppose that the Shape class includes a draw() method for drawing the figure. Then we can write a generic method for drawing all the figures in a collection of Shapes:
void drawAllShapes( Collection shapeCollection ) {
// Precondition: Every item in shapeCollection is non-null
// and belongs to the class Shape.
Iterator iter = shapeCollection.iterator();
while ( iter.hasNext() ) {
Shape figure = (Shape)iter.next();
figure.draw();
}
}
The precondition of this method points out that the method will fail if the method contains an item that does not belong to class Shape. When that item is encountered, the type-cast "(Shape)iter.next()" will cause an exception of type ClassCastException. Although it's unfortunate that we can't have a "Collection of Shapes" in Java, rather than a "Collection of Objects", it's not a big problem in practice. You just have to be aware of what type of objects you are storing in your collections.
Equality and ComparisonThe discussion of methods in the Collection interface had an unspoken assumption: It was assumed that it's known what it means for two objects to be "equal." For example, the methods coll.contains(object) and coll.remove(object) look for an item in the collection that is equal to object. However, equality is not such a simple matter. The obvious technique for testing equality -- using the == operator -- does not usually give a reasonable answer when applied to objects. The == operator tests whether two objects are identical in the sense that they share the same location in memory. Usually, however, we want to consider two objects to be equal if they represent the same value, which is a very different thing. Two values of type String should be considered equal if they contain the same sequence of characters. The question of whether those characters are stored in the same location in memory is irrelevant. Two values of type Date should be considered equal if they represent the same time. The Object class defines a boolean-valued method equals(Object) for testing whether one object is equal to another. For the purposes of collections, obj1 and obj2 are considered to be equal if they are both null, or if they are both non-null and obj1.equals(obj2) is true. In the Object class, obj1.equals(obj2) is defined to be the same as obj1 == obj2. However, for most sub-classes of Object, this definition is not reasonable, and it should be overridden. The String class, for example, overrides equals() so that for a String str, str.equals(obj) if obj is also a String and obj contains the same sequence of characters as str. If you write your own class, you might want to define an equals() method in that class to get the correct behavior when objects are tested for equality. For example, a Card class that will work correctly when used in collections could be defined as: public class Card { // Class to represent playing cards.
int suit; // Number from 0 to 3 that codes for the suit --
// spades, diamonds, clubs or hearts.
int value; // Number from 1 to 13 that represents the value.
public boolean equals(Object obj) {
if (obj == null || ! (obj instanceof Card) ) {
// obj can't be equal to this Card if obj
// is not a Card, or if it is null.
return false;
}
else {
Card other = (Card)obj; // Type-cast obj to a Card.
if (suit == other.suit && value == other.value) {
// The other card has the same suit and value as
// this card, so they should be considered equal.
return true;
}
else
return false;
}
}
... // other methods and constructors
}
Without the equals() method in this class, methods such as contains() and remove() from the Collection interface will not work as expected for values of type Card. A similar concern arises when items in a collection are sorted. Sorting refers to arranging a sequence of items in ascending order, according to some criterion. The problem is that there is no natural notion of ascending order for arbitrary objects. Before objects can be sorted, some method must be defined for comparing them. Objects that are meant to be compared should implement the interface java.lang.Comparable. This interface defines one method: public int compareTo(Object obj) The value returned by obj1.compareTo(obj2) should be zero if and only if the objects are equal (that is, if obj1.equals(obj2) is true). It should be negative if and only if obj1 comes before obj2, when the objects are arranged in ascending order. And it should be positive if and only if obj1 comes after obj2. In general, it should be considered an error to call obj1.compareTo(obj2) if obj2 is not of the same type as obj1. The String class implements the Comparable interface and defines compareTo in a reasonable way. If you define your own class and want to be able to sort objects belonging to that class, you should do the same. For example: class FullName implements Comparable {
// Represents a full name consisting of a first
// name and a last name.
String firstName, lastName;
public boolean equals(Object obj) {
if (obj == null || ! (obj instanceof FullName)) {
return false;
}
else {
FullName other = (FullName)obj;
return firstName.equals(other.firstName)
&& lastName.equals(other.lastName);
}
}
public void compareTo(Object obj) {
Fullname other = (FullName)obj;
// Will cause an error if obj is not a FullName.
if ( lastName.compareTo(other.lastName) < 0 ) {
// If lastName comes before the last name of
// the other object, then this FullName comes
// before the other FullName. Return a negative
// value to indicate this.
return -1;
}
if ( lastName.compareTo(other.lastName) > 0 ) {
// If lastName comes after the last name of
// the other object, then this FullName comes
// after the other FullName. Return a positive
// value to indicate this.
return 1;
}
else {
// Last names are the same, so base the comparison
// on the first names.
return firstName.compareTo(other.firstName);
}
}
... // other methods and constructors
}
There is another way to allow for comparison of objects in Java, and that is to provide a separate object that is capable of making the comparison. The object must implement the interface java.util.Comparator, which defines the method: public int compare(Object obj1, Object obj2) This method compares two objects and returns a value that is negative, or zero, or positive depending on whether obj1 comes before obj2, or is the same as obj2, or comes after obj2. Comparators are useful for comparing objects that do not implement the Comparable interface and for defining several different orderings on the same collection of objects. In the next two sections, we'll see how Comparable and Comparator are used in the context of collections and maps. Wrapper ClassesAs noted above, Java's generic programming does not apply to the primitive types. Before leaving this section, we should try to address this problem. You can't store an integer in a generic data structure designed to hold Objects. On the other hand, there is nothing to stop you from making an object that contains an integer and putting that object into the data structure. In the simplest case, you could define a class that does nothing but contain an integer: public class IntContainer {
public int value;
}
In fact, Java already has a class similar to this one. An object belonging to the class java.lang.Integer contains a single int. It is called a wrapper for that int. The int value is provided as a parameter to the constructor. For example, Integer val = new Integer(17); creates an Integer object that "wraps" the number 17. The Integer object can be used in generic data structures and in other situations where an object is required. The int value is stored in a private final instance variable of the Integer object. If val refers to an object of type Integer, you can find out what int it contains by calling the instance method val.intValue(). There is no way to change that value. We say that an Integer is an immutable object. That is, after it has been constructed, there is no way to change it. (Similarly, an object of type String is immutable.) There are wrapper classes for all the primitive types. All objects belonging to these classes are immutable. The wrapper class for values of type double is java.lang.Double. The value stored in an object of type Double can be retrieved by calling the instance method doubleValue(). The wrapper classes define a number of useful methods. Some of them exist to support generic programming. For example, the wrapper classes all define instance methods equals(Object) and compareTo(Object) in a reasonable way. Other methods in the wrapper classes are utility functions for working with the primitive types. For example, we encountered the static methods Integer.parseInt(String) and Double.parseDouble(String) in Section 7.4. These functions are used to convert strings such as "42" or "2.71828" into the numbers they represent.
[ Next Section | Chapter Index | Main Index ] |
|
|
||||||||||||||||||||||||||||||
|
Home | JSP | EJB | JDBC | Java Servlets | WAP | Free JSP Hosting | Search Engine | News Archive | Jboss 3.0 tutorial | Free Linux CD's | Forum | Blogs |
||||||||||||||||||||||||||||||
Send your comments, Suggestions or Queries regarding this site at roseindia_net@yahoo.com.
Copyright © 2007. All rights reserved.