Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

The values of boxed primitives cannot be directly compared using the == and != operators because these operators compare object references , rather than object values. Programmers could can find this behavior surprising because autoboxing memoizes, or caches, the values of some primitive variables. Consequently, reference comparisons and value comparisons produce identical results for the subset of values that are memoized.

Wiki Markup
Autoboxing automatically wraps a value of a primitive type with the corresponding wrapper object. The _Java Language Specification_ (JLS) [§5.1.7, "Boxing Conversion,"|http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.7] of the _Java Language Specification_ \[[JLS 2005|AA. Bibliography#JLS 05]\], explains which primitive values are memoized during autoboxing: 

...

Use of the == and != operators for comparing the values of boxed primitive types that are not fully memoized is permitted only when the range of values represented is guaranteed to be within the ranges specified by the Java Language Specification JLS to be fully memoized.

Use of the == and != operators for comparing the values of boxed primitive types is not allowed in all other cases.

...

Less memory-limited implementations could, for example, cache all characters and shorts, as well as integers and longs in the range of -32K to +32K.

Code that depends on implementation-defined behavior is non-portable.

...

The Integer class is only guaranteed to cache integer values from -128 to 127, which can result in equivalent values outside this range comparing as unequal when tested using the equality operators. For example, a Java Virtual Machine (JVM) that did not cache any other values when running this program would output

...

This noncompliant code example attempts to count the number of indices in arrays list1 and list2 that have equivalent values. Recall that class Integer is required to memoize only those integer values in the range -128 to 127; it might return a non-unique nonunique object for any value outside that range. Consequently, when comparing autoboxed integer values outside that range, the == operator might return false, and the output of this example might be example could deceptively output 0.

Code Block
bgColor#FFCCCC
public class Wrapper {
  public static void main(String[] args) {
    // Create an array list of integers, where each element 
    // is greater than 127
    ArrayList<Integer> list1 = new ArrayList<Integer>();
    for (int i = 0; i < 10; i++) {
      list1.add(i + 1000);
    }

    // Create another array list of integers, where each element
    // has the same value as the first list
    ArrayList<Integer> list2 = new ArrayList<Integer>();
    for (int i = 0; i < 10; i++) {
      list2.add(i + 1000);
    }

    // Count matching values.
    int counter = 0;
    for (int i = 0; i < 10; i++) {
      if (list1.get(i) == list2.get(i)) {  // uses '=='
        counter++;
      }
    }

    // printPrint the counter: 0 in this example
    System.out.println(counter);
  }

}

If However, if the particular JVM running this code memoized integer values from -32,768 to 32,767, all of the int values in the example would have been autoboxed to singleton Integer objects, and the example code would have operated as expected. Using reference equality instead of object equality requires that all values encountered fall within the interval of values memoized by the JVM. The Java Language Specification JLS lacks a specification of this interval; rather, it specifies a minimum range that must be memoized. Consequently, successful prediction of this program's behavior would require implementation-specific details of the JVM.

...

Code Block
bgColor#CCCCFF
public class Wrapper {
 public static void main(String[] args) {
   // Create an array list of integers, where each element
   // is greater than 127
   ArrayList<Integer> list1 = new ArrayList<Integer>();

   for (int i = 0; i < 10; i++) {
     list1.add(i + 1000);
   }

   // Create another array list of integers, where each element
   // has the same value as the first one
   ArrayList<Integer> list2 = new ArrayList<Integer>();
   for (int i = 0; i < 10; i++) {
     list2.add(i + 1000);
   }
 
   // Count matching values
   int counter = 0;
   for (int i = 0; i < 10; i++) {
     if (list1.get(i).equals(list2.get(i))) {  // uses 'equals()'
       counter++;
     }
   }
 
   // printPrint the counter: 10 in this example
   System.out.println(counter);
 }
}

...

Code Block
bgColor#FFCCCC
public void exampleEqualOperator(){

  Boolean b1 = new Boolean("true");
  Boolean b2 = new Boolean("true");

  if ( b1 == b2) ){           // never equal
    System.out.println("Never printed");
  }
}

Compliant Solution (new Boolean)

...

Code Block
bgColor#CCCCFF
public void exampleEqualOperator(){

  Boolean b1 = true; // Or Boolean.True
  Boolean b2 = true; // Or Boolean.True
	
  if (b1 == b2) {         // always equal
    System.out.println("Will always be printed");
  }
}

Exceptions

EXP03-EX0 In the unusual case where a program is guaranteed to execute only on a single implementation, it is permissible to depend upon on implementation-specific ranges of memoized vulnerabilitiesvalues.

Risk Assessment

Using the equivalence operators to compare values of boxed primitives can lead to erroneous comparisons.

...

Related Guidelines

MITRE CWE

CWE-595, ". Comparison of Object References Instead of Object Contents" object references instead of object contents

 

CWE-597, ". Use of Wrong Operator in String Comparison" wrong operator in string comparison

Bibliography

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="5c7878f5b8680575-aaea1f20-489346f7-bd29b37b-cd4f49e5495e353865526daf"><ac:plain-text-body><![CDATA[

[[Bloch 2009

AA. Bibliography#Bloch 09]]

4. ", Searching for the One"

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="1f332c5685894a52-83194950-4246457d-b02fbf0d-58e82b8c6645fe8417558e22"><ac:plain-text-body><![CDATA[

[[JLS 2005

AA. Bibliography#JLS 05]]

[§5.1.7, " Boxing Conversion"

http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.7]

]]></ac:plain-text-body></ac:structured-macro>

<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="82ed853948a782f1-b0b703a6-4c8c4da6-905a80a5-52397b96210fc15815c422d4"><ac:plain-text-body><![CDATA[

[[Pugh 2009

AA. Bibliography#Pugh 09]]

Using == to compare objects rather than .equals

]]></ac:plain-text-body></ac:structured-macro>

...