Showing posts with label equality. Show all posts
Showing posts with label equality. Show all posts

Saturday, 14 May 2011

equals() vs ==

Object class has a default method which looks like this :

public boolean equals(Object other)
{
return this==other;
}


== tests identity of the object, i.e. whether the 2 references are pointing the same object or not.

The default implementation of equals() is based on the == operator: Two objects are equal if and only if they are the same object. Naturally, most classes should define their own alternative implementation of this important method.

Steps that need to be taken into consideration while implementing equals method

  1. Use the == operator to check whether the incoming object is null. Its kind of performance optimization.
    public boolean equals(Object incomingObject) {
    if (incomingObject == null) return false;
    }


  2. Use the == operator to check if the argument is a reference to this object. If so, return true. This is just a performance optimization, but one that is worth doing if the comparison is potentially expensive.

    public boolean equals(Object incomingObject){
    if(this==incomingObject)
    return true;
    }


  3. Use the instanceof operator to check if the argument has the correct type.
    If not, return false. Typically, the correct type is the class in which the method occurs. Occasionally, it is some interface implemented by this class. Use an interface if the class implements an interface that refines the equals contract to permit comparisons across classes that implement the interface. Collection interfaces such as Set, List, Map, and Map.Entry have this property.

    public boolean equals(){
    if (!(incomingObject instanceof MyClass)) return false;
    }

    Note  : I have not checked like this:

    if(incomingObject instanceof MyClass)
    do something;

    The reason being instanceof returns true even when incomingObject is subclass object. So its important to check whether the object is type of class we are writing equals or not. If not, what is the point of writing further logic. Just return false, and leave.

  4. Cast the argument to the correct type. Because this cast was preceded by an instanceof test, it is guaranteed to succeed.

    public boolean equals(Object incomingObject){
    MyClass mc = (MyClass) incomingObject;
    }



  5. For each “significant” field in the class, checks if that field of the argument matches the corresponding field of this object. If all these tests succeed, return true; otherwise, return false


  6. When you are finished writing your equals method, ask yourself three questions: Is it symmetric? Is it transitive? Is it consistent?

Writing the complete equals function


Finishing whole steps:


public boolean equals(Object o) {
if (o == null) return false;
if (o == this) return true;
if (!(o instanceof MyClass)) return false;
MyClass mc = (MyClass) o;
return ... // compare members of mc
}

Saturday, 30 April 2011

Immutable Objects / Wrapper Class Caching

Since Java 5, wrapper class caching was introduced.
The following is an examination of the cache created by an inner class, IntegerCache, located in the Integer cache. For example, the following code will create a cache:
Integer myNumber = 10; or Integer myNumber = Integer.valueOf(10);

256 Integer objects are created in the range of -128 to 127 which are all stored in an Integer array. This caching functionality can be seen by looking at the inner class, IntegerCache, which is found in Integer:
private static class IntegerCache 
{
private IntegerCache(){}

static final Integer cache[] = new Integer[-(-128) + 127 + 1];

static
{
for(int i = 0; i < cache.length; i++)
cache[i] = new Integer(i - 128);
}
}

public static Integer valueOf(int i)
{
final int offset = 128;
if (i >= -128 && i <= 127) // must cache
{
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}


So when creating an object using Integer.valueOf or directly assigning a value to an Integer within the range of -128 to 127 the same object will be returned. Therefore, consider the following example:

Integer i = 100;
Integer p = 100;
if (i == p)
System.out.println("i and p are the same.");
if (i != p)
System.out.println("i and p are different.");
if(i.equals(p))
System.out.println("i and p contain the same value.");

The output is:
i and p are the same.
i and p contain the same value.


So this result is similar to the result as shown here for strings.

It is important to note that object i and p only equate to true because they are the same object, the comparison is not based on the value, it is based on object equality. If Integer i and p are outside the range of -128 or 127 the cache is not used, therefore new objects are created. When doing a comparison for value always use the “.equals” method. It is also important to note that instantiating an Integer does not create this caching. So consider the following example:

Integer i = new Integer (100);
Integer p = new Integer(100);
if(i==p)
System.out.println(“i and p are the same object”);
if(i.equals(p))
System.out.println(“ i and p contain the same value”);


In this circumstance, the output is only: i and p contain the same value

The other wrapper classes (Byte, Short, Long, Character) also contain this caching mechanism OR constant pooling mechanism. The Byte, Short and Long all contain the same caching principle to the Integer object. The Character class caches from 0 to 127. The negative cache is not created for the Character wrapper as these values do not represent a corresponding character. There is no caching for the Float object.

BigDecimal also uses caching but uses a different mechanism. While the other objects contain a inner class to deal with caching this is not true for BigDecimal, the caching is pre-defined in a static array and only covers 11 numbers, 0 to 10:

 
// Cache of common small BigDecimal values.
private static final BigDecimal zeroThroughTen[] = {
new BigDecimal(BigInteger.ZERO, 0, 0),
new BigDecimal(BigInteger.ONE, 1, 0),
new BigDecimal(BigInteger.valueOf(2), 2, 0),
new BigDecimal(BigInteger.valueOf(3), 3, 0),
new BigDecimal(BigInteger.valueOf(4), 4, 0),
new BigDecimal(BigInteger.valueOf(5), 5, 0),
new BigDecimal(BigInteger.valueOf(6), 6, 0),
new BigDecimal(BigInteger.valueOf(7), 7, 0),
new BigDecimal(BigInteger.valueOf(8), 8, 0),
new BigDecimal(BigInteger.valueOf(9), 9, 0),
new BigDecimal(BigInteger.TEN, 10, 0),
};


As per Java Language Specification(JLS) the values discussed above are stored as immutable wrapper objects. This caching has been created because it is assumed these values / objects are used more frequently.

String equality and interning in java

Strings in Java are objects, but resemble primitives (such as ints or chars) in that Java source code may contain String literals, and Strings may be concatenated using the “+” operator. These are convenient features, but the similarity of Strings to primitives sometimes causes confusion when Strings are compared.

As we saw here, how java deals with string comparisons. Lets understand the case 2, where == operator returns true for 2 different references having same values.
To save memory (and speed up testing for equality), Java supports “interning” of Strings. When the intern() method is invoked on a String, a lookup is performed on a table of interned Strings. If a String object with the same content is already in the table, a reference to the String in the table is returned. Otherwise, the String is added to the table and a reference to it is returned. The result is that after interning, all Strings with the same content will point to the same object. This saves space, and also allows the Strings to be compared using the == operator, which is much faster than comparison with the equals(Object) method.

Confusion can arise because Java automatically interns String literals. This means that in many cases, the == operator appears to work for Strings in the same way that it does for ints or other primitive values. Code written based on this assumption will fail in a potentially non-obvious way when the == operator is used to compare Strings with equal content but contained in different String instances.
Following test cases show how interning can be performed by java:
Consider following string i.e “A String":
 
String aString = "A String"; 



Case 1: Concatenated string


String aConcatentatedString = "A" + " " + "String";


aString == aConcatentatedString       : true
aString.equals(aConcatentatedString) : true

Case 2: Runtime string




String aRuntimeString = new String("A String");


aString == aConcatentatedString       : false
aString.equals(aConcatentatedString) : true


Case 3: Interned string


String anInternedString = aRuntimeString.intern();

aString == aConcatentatedString : true
aString.equals(aConcatentatedString) : true



Case 4: External strings , eg. 1st argument of main method


String firstArg = args[0];


aString == aConcatentatedString : false
aString.equals(aConcatentatedString) : true


Case 5: Using intern on external strings


String firstArgInterned = firstArg.intern();

aString == aConcatentatedString : true
aString.equals(aConcatentatedString) : true




So we can see that explicitly invoking intern() returns a reference to the interned String.

Similar to string, there is a pool of integers, bytes, etc and other value based classes like bigdecimal. See here for more on this.

== vs equals method in case of String and primitive types

Object equality is tested using == operator. Here it is checked whether 2 references are pointing to same object or not.
While value equality is tested using the .equals(Object) method.
So consider following code snippet:
String one = new String("abc");
String two = new String("abc");


So here

one==two is false

one.equals(two) is true

one==two, because we are creating 2 new objects as specified by new keywords. But as their values are same one.equals(two) returns true because values are same.

Now if we say:

String three = one;



Now both one==two as well one.equals(two) returns true.

Using new keyword vs not using it to create 2 strings


Now consider 2 cases, when we create 2 strings with same value, but first using new keyword and then not using new keyword:

Case1 : Using new keyword:

String data = new String("123");
String moreData = new String("123");



Here data==moreData is false.

Case 2 : Not using new keyword:

String letters = "abc";
String moreLetters = "abc";


Here data==moreData is true.

Why?

Explaining the case 1 is very simple. Because it falls inline with our earlier explanation of == and != operators. But how can we explain case 2, where string comparison using == operator, returns true.

This is due to the compiler and runtime efficiency. In the compiled class file only one set of data "abc" is stored, not two. In this situation only one object is created, therefore the equality is true between these object. Read here for more on this. Not only strings , compiler also support this type of constant pooling for integers, bytes and other value types. See here for this.


Note:

Even though one set of data "123" is stored in the class, this is still treated differently at runtime. An explicit instantiation is used to create the String objects. Therefore, in this case, two objects have been created, so the equality is false. It is important to note that "==" is always used for object equality and does not ever refer to the values in an object. Always use .equals when checking looking for a "meaningful" comparison.

equals() for String, StringBuilder and StringBuffer

Rule of equality in java:

Equals method should be used for checking the logical equality of objects of a class but it does not mean that every class is a candidate for overridden equals method

So if you go and see the source code of StringBuffer/StringBuilder classes in Java, you will find that equals() method is not overridden but it is overridden in the String class.

public class Test{

public static void main (String args[]) {
String str1 = "abcd";
String str2 = "abcd";
System.out.println(str1.equals(str2));

StringBuffer sb1 = new StringBuffer(str1);
StringBuffer sb2 = new StringBuffer(str1);
System.out.println(sb1.equals(sb2));
}
}

Output:

true
false


For String objects, the logical equality would mean the contents of the string should be same.

For StringBuffer and StringBuilder we have following facts:
  • Logical equality means that both objects were configured and assembled using the same String instance and not whether the contents of those String objects is same.
  • The intended use of StringBuffer and StringBuilder is for maintaining a buffer of characters which may change with time.
Both the above statements contradict with each other because the String object being used for building a StringBuffer/StringBuilder object can change very frequently and it does not make any sense to compare the StringBuffer/StringBuilder objects.

This is the reason that the equals() method is not overridden in StringBuffer/StringBuilder classes.

It should be noted that there a misconception that is going around the developer community regarding the above reason which says that then why an ArrayList/Date overrides the equals() method as it too can change with time. Here are a few reasons to clear that thought which in fact is natural:
  • StringBuffer/StringBuilder classes are intended to be used as temporary buffer and change more frequently than classes like ArrayList and Date
  • While one may like to perform various operations like serialization, making clones, being used as keys in collections or stored in database but one is very unlikely to perform similar operations with StringBuffer/StringBuilder classes.
Also String may be treated as value object, where value equality means, object equality. Read here for more on value objects.