Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Edited by NavBot (vkp) v1.0

Wiki Markup
Keys of ordered sets and maps should typically be immutable. If this is not possible, ensure that the {{equals()}} and {{compareTo()}} methods do not take into account the mutable state while comparing objects. Failure to do this, for example, can produce inconsistent orderings in collections. The documentation of {{java.util.Interface Set<E>}} and {{java.util.Interface Map<K,V>}} warn against this \[[API 2006|AA. Java References#API 06]\]:

Note: great care must be exercised if mutable objects are used as map keys. The behavior of a map is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is a key in the map. A special case of this prohibition is that it is not permissible for a map to contain itself as a key. While it is permissible for a map to contain itself as a value, extreme caution is advised: the equals and hashCode methods are no longer well defined on such a map.

Noncompliant Code Example

This noncompliant code example defines a mutable class Employee that consists of the fields name and salary whose values can be changed using the respective setters. The equals() method is overridden to provide a comparison facility by employee name.

Code Block
bgColor#FFCCCC
// Mutable class Employee
class Employee {
  private String name;
  private double salary;
 
  Employee(String empName, double empSalary) {
    this.name = empName;
    this.salary = empSalary;
  }
  
  public void setEmployeeName(String empName) {
    this.name = empName;   
  }

  // ...

  @Override public boolean equals(Object o) {
    if (!(o instanceof Employee)) {
      return false;
    }
      
    Employee emp = (Employee)o;
    return emp.name.equals(name);
  } 
}

// Client code
Map<Employee, Calendar> map = new ConcurrentHashMap<Employee, Calendar>();
// ...

Use of the Employee object as a key for the map is insecure because the properties of the object may change when an ordering has already been established. For example, a client may modify the name field when the last name of an employee changes. Consequently, clients may observe non-deterministic behavior.

Compliant Solution

This compliant solution adds a final field employeeID that is immutable after initialization. The equals() method compares Employee objects on the basis of this field.

Code Block
bgColor#ccccff
// Mutable class Employee
class Employee {
  private String name;
  private double salary;
  private final long employeeID;  // Unique for each Employee

  Employee(String name, double salary, long empID) {
    this.name = name;
    this.salary = salary;
    this.employeeID = empID;
  }
 
  // ...

  @Override public boolean equals(Object o) {
    if (!(o instanceof Employee)) {
      return false;
    }
      
    Employee emp = (Employee)o;
    return emp.employeeID == employeeID;
  } 
}

// Client code remains same
Map<Employee, Calendar> map = new ConcurrentHashMap<Employee, Calendar>();
// ... 

The Employee class can now be safely used as a key for the map in the client code.

Risk Assessment

Failure to ensure that the keys used in a comparison operation are immutable can lead to non-deterministic behavior.

Guideline

Severity

Likelihood

Remediation Cost

Priority

Level

OBJ15-J

low

probable

high

P2

L3

Automated Detection

The Coverity Prevent Version 5.0 MUTABLE_COMPARISON checker can detect the instances where compareTo method is reading from a non constant field. If the non-constant field is modified, the value of compareTo might change, which may break program invariants.

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

References

Wiki Markup
\[[API 2006|AA. Java References#API 06]\] {{java.util.Interface Set<E>}} and {{java.util.Interface Map<K,V>}}


OBJ14-J. Encapsulate the absence of an object by using a Null Object      08. Object Orientation (OBJ)      09. Input Output (FIO)