Essential Tools Module User's Guide : Chapter 6 Collection Classes : Retrieving Objects in Collections
Retrieving Objects in Collections
We have defined the major task of collection classes as storing and retrieving objects. How you retrieve or find an object depends on its properties. Every object you create has three properties associated with it:
1. Type: for example, an RWCString or a double. In C++, the type of an object is set at creation, and cannot change.
2. State: the value of the string. The values of all the instance variables or attributes of an object determine its state. These can change.
3. Identity: the unique definition of the object for all time. Languages use different methods for establishing an object's identity. C++ always uses the object's address. Each object is associated with one and only one address. Note that the reverse is not always true, because of inheritance. Generally, an address and a type are both necessary to disambiguate the object you mean within an inheritance hierarchy. Because of multiple inheritance, it may be necessary to know not only an object's type, but also its location within an inheritance tree in order to disambiguate which object you mean.
Retrieval Methods
Based on the properties of an object, there are two general methods for finding or retrieving it. Some collection classes can support either, some only one. The important thing for you to keep in mind is which one you mean. The two methods are:
Find an object with a particular state. For example, test two strings for the same value. In the literature, this is variously referred to as two objects testing isEqual, having equality, compares equal, having the same value, or testing true for the == operator. Here, we refer to the two objects testing equal as isEqual. In general, we need some knowledge of the type of each object—or subtype, in the case of inheritance—in order to find the appropriate instance variables to test for equality. The Rogue Wave collection classes allow a generalized test of equality; it is up to you to define what it means for two objects to “be equal”. A bit-by-bit comparison of the two objects is not done.
Find a particular object; that is, one with the same identity as the object being compared. In the literature, this is referred to as two objects testing isSame, having the same identity, or testing true for the == operator. We refer to this as two objects having the same identity. Note that because value-based collection classes make a copy of an inserted object, finding an object in a value-based collection class with a particular identity is meaningless.
In C++, to test for identity—that is, to test whether two objects are the same object—you must see if they have the same address. Because of multiple inheritance, the address of a base class and its associated derived class may not be the same. Therefore, if you compare two pointers (addresses) to test for identity, the types of the two pointers should be the same.
Smalltalk uses the operator = to test for equality, and the operator == to test for identity. In the C++ world, however, operator = is firmly attached to assignment, and operator == to some kind of equality of values. We have taken the C++ approach. At Rogue Wave, the operator == generally means test for equality of values (isEqual) when applied to two classes, and test for identity when applied to two pointers.
Whether to test for equality or identity depends on the context of your problem. Here are some examples that can clarify which to choose.
The first example shows when you should test for equality. Suppose you are maintaining a mailing list. Given a person's name, you want to find his or her address. In this case, you search for a name that is equal to the name at hand. An RWHashDictionary would be appropriate. The key to the dictionary would be the name, the value would be the address.
In the next example, you would test for identity. Suppose you are writing a hypertext application, and need to know in which document a particular graphic occurs. You could keep an RWHashDictionary of graphics and their corresponding documents. In this case, however, you need an RWIdentityDictionary because you need to know in which document a particular graphic occurs. The graphic acts as the key, the document as the value.
Maintaining a disk cache? You might want to know whether a particular object is resident in memory. In this case, an RWIdentitySet is appropriate. Given an object, you can check to see whether it exists in memory—another identity test.