Field Comparisons – Object Comparison


Field Comparisons

Equivalence comparison involves comparing relevant fields from both objects to determine if their logical states match. For fields that are of primitive data types, their primitive values can be compared. Instances of the class UsableVNO in Example 14.3 have fields of primitive data types only. Values of corresponding fields can be compared to test for equality between two UsableVNO objects, as shown at (4):

Click here to view code image

return (this == obj)                                // (2)
    || (obj instanceof UsableVNO vno                // (3)
        && this.patch    == vno.patch               // (4)
        && this.revision == vno.revision
        && this.release  == vno.release);

If all field comparisons evaluate to true, the equals() method returns true.

For fields that are references, the objects referenced by the references can be compared. For example, if the UsableVNO class declares a field called productInfo, which is a reference, the following expression can be used:

Click here to view code image

(this.productInfo != null && this.productInfo.equals(vno.productInfo)))

In order to avoid a NullPointerException being thrown, the equals() method is not invoked if the this.productInfo reference is null. However, the Objects.equals() utility method makes this comparison much simpler, as it provides null-safe object value equality operation, only invoking the equals() method on the first productInfo field, if both arguments are not null:

Click here to view code image

Objects.equals(this.productInfo, vno.productInfo)

Exact comparison of floating-point values should not be done directly on the values, but on the integer values obtained from their bit patterns (see static methods Float.floatToIntBits() and Double.doubleToLongBits() in the Java SE Platform API documentation). This technique eliminates certain anomalies in floating-point comparisons that involve a NaN value or a negative zero (see also the equals() method in Float and Double classes).

Only fields that have significance for the equivalence relation should be considered. Derived fields, whose computation is dependent on other field values in the object, might be redundant to include. Including only the significant fields may be prudent. Computing the equivalence relation should be deterministic; therefore, the equals() method should not depend on unreliable resources, such as network access.

The order in which the comparisons of the significant fields are carried out can influence the performance of the equals comparison. Fields that are most likely to differ should be compared as early as possible, in order to short-circuit the computation. In our example, patch numbers evolve faster than revision numbers, which, in turn, evolve faster than release numbers. This order is reflected in the return statement at (4) in Example 14.3.

Above all, an implementation of the equals() method must ensure that the equivalence relation is fulfilled.

Example 14.4 is a client that uses the class UsableVNO from Example 14.3. This client runs the same tests as the client in Example 14.2. The difference is that the class UsableVNO overrides the equals() method.

Example 14.4 Implications of Implementing the equals() Method

Click here to view code image

import static java.lang.System.out;
import java.util.*;
public class TestUsableVNO {
  public static void main(String[] args) {
    // Print name of version number class:
    out.println(UsableVNO.class);
    // Three individual version numbers.
    UsableVNO svno1 = new UsableVNO(9,1,1);                               // (1)
    UsableVNO svno2 = new UsableVNO(9,1,1);                               // (2)
    UsableVNO svno3 = new UsableVNO(6,6,6);                               // (3)
    // An array of version numbers.
    UsableVNO[] versions =  new UsableVNO[] {                             // (4)
        new UsableVNO( 3,49, 1), new UsableVNO( 8,19,81),
        new UsableVNO( 2,48,28), new UsableVNO(10,23,78),
        new UsableVNO( 9, 1, 1)};
    out.printf (”  svno1: %s, svno2: %s, svno3: %s%n”, svno1, svno2, svno3);
    out.println(“Test object reference equality:”);                       // (5)
    out.println(”  svno1 == svno2:      ” + (svno1 == svno2));            // (6)
    out.println(”  svno1 == svno3:      ” + (svno1 == svno3));            // (7)
    out.println(“Test object value equality:”);
    out.println(”  svno1.equals(svno2): ” + svno1.equals(svno2));         // (8)
    out.println(”  svno1.equals(svno3): ” + svno1.equals(svno3));         // (9)
    out.println();
    // Search key:
    UsableVNO searchKey = new UsableVNO(9,1,1);                           // (10)
    // Searching in an array:
    boolean found = false;
    for (UsableVNO version : versions) {
      found = searchKey.equals(version);                                  // (11)
      if (found) break;
    }
    out.println(“Array: ” + Arrays.toString(versions));                   // (12)
    out.printf(”  Search key %s found in array:    %s%n%n”,               // (13)
                searchKey, found);
    // Searching in a list:
    List<UsableVNO> vnoList = Arrays.asList(versions);                    // (14)
    out.println(“List:  ” + vnoList);
    out.printf(”  Search key %s contained in list: %s%n%n”, searchKey,
                vnoList.contains(searchKey));                             // (15)
  }
}

Output from the program:

Click here to view code image

class UsableVNO
  svno1: (9.1.1), svno2: (9.1.1), svno3: (6.6.6)
Test object reference equality:
  svno1 == svno2:      false
  svno1 == svno3:      false
Test object value equality:
  svno1.equals(svno2): true
  svno1.equals(svno3): false
Array: [(3.49.1), (8.19.81), (2.48.28), (10.23.78), (9.1.1)]
  Search key (9.1.1) found in array:    true
List:  [(3.49.1), (8.19.81), (2.48.28), (10.23.78), (9.1.1)]
  Search key (9.1.1) contained in list: true

The output from the program shows that object value equality is compared correctly. Object value equality is now based on identical states, as defined by the equals() method.

The search for a UsableVNO object in an array or a list of UsableVNO objects is successful, since the equals comparison is based on the states of the objects and not on their reference values.

Next, we look at how to fix the version numbers so that they can be used for searching in sets and maps.

Leave a Reply

Your email address will not be published. Required fields are marked *