32
StringBuffer sb1 = new StringBuffer("Java");
StringBuffer sb2 = new StringBuffer("Java");
System.out.println(sb1 == sb2);
System.out.println(sb1.equals(sb2));

Here both are returning false. How is it possible?

teabot
  • 15,358
  • 11
  • 64
  • 79
GuruKulki
  • 25,776
  • 50
  • 140
  • 201

11 Answers11

43

The equals method of StringBuffer is not overridden from Object, so it is just reference equality, i.e., the same as using ==. I suspect the reason for this is that StringBuffer is modifiable, and overriding equals is mostly useful for value-like classes that you might want to use as keys (though lists also have an overridden equals and StringBuffer is kind of a list, so this is a bit inconsistent).

JaakkoK
  • 8,247
  • 2
  • 32
  • 50
  • 3
    As I just fell into the same pit, I'd like to remark that this is simply a **stupid design decision**: Instead of `sb1.equals(sb2)`I'll have to write `sb1.toString().equals(sb2.toString())` (because this is what actually matters). Maybe it's also the reason: Besides the current contents, there are internal attributes (like buffer capacity) which may or may not affect the view of equality. Finally: `hashCode()` has the same problem. – U. Windl May 28 '18 at 09:03
31

You are comparing the references to the StringBuffer objects rather than the actual strings within the StringBuffer.

System.out.println(sb1.toString().equals(sb2.toString())) would return true and I assume this is what you had expected or wanted to achieve.

Mark
  • 28,783
  • 8
  • 63
  • 92
11
 1.  System.out.println(sb1 == sb2);  

StringBuffer's equals method returns true only when a StringBuffer object is compared with itself. It returns false when compared with any other StringBuffer, even if the two contain the same characters.

This is because "==" checks the reference equality and since both sb1 and sb2 are different object references, so the output in this case is "false"

Still if you want to check if the content is equal in these two StringBuffer Objects, you can use this:

sb1.toString().equals(sb2.toString())

2. System.out.println(sb1.equals(sb2));

This is giving output as "false" because .equals() method has not been overridden in the StringBuffer Class. So it is using the .equals() method from its parent "Object" class. In the object class .equals() has been written to check the reference equality.

Note that sb3.equals(sb4) will return "true" in case of String. Because .equals() method has been overridden in String class to check and match the content of two different Strings.

Ashish Gupta
  • 567
  • 4
  • 7
9

The simple answer is that StringBuffer (and StringBuilder) do not override the base semantics of Object.equals(). So equals on a StringBuffer will simply compare object references.

In fact, String, StringBuffer, StringBuilder and CharBuffer all implement the CharSequence interface, and the javadoc for this interface says this:

This interface does not refine the general contracts of the equals and hashCode methods. The result of comparing two objects that implement CharSequence is therefore, in general, undefined. Each object may be implemented by a different class, and there is no guarantee that each class will be capable of testing its instances for equality with those of the other. It is therefore inappropriate to use arbitrary CharSequence instances as elements in a set or as keys in a map.

Also note that javadocs for StringBuffer (and StringBuilder) explicitly states this:

API Note:

StringBuffer implements Comparable but does not override equals. Thus, the natural ordering of StringBuffer is inconsistent with equals. Care should be exercised if StringBuffer objects are used as keys in a SortedMap or elements in a SortedSet.


But why?

At the heart of it is the fact that String is immutable and StringBuffer / StringBuilder are mutable.

  • If two String objects have the same characters, they will always have the same characters. So it is natural to treat them as equal ... and that is what String::equals(Object) does.

  • If two StringBuffer objects can have the same characters now, and different characters a moment later ... due to a mutating operation performed by another thread. An implementation of equals(Object) for StringBuffer that was sensitive to the (changing) content would be problematic. For example:

    if (buf1.equals(buf2)) {
        // Do something to one of the StringBuffer objects.
    }
    

    has a potential race condition. Another example is use of StringBuffer instances as keys in HashTable or HashMap.

At any rate, a deliberate design decision taken a very long time ago that StringBuffer and StringBuilder would not override the Object::equals and Object::hashCode methods.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
3

StringBuffer seems to have no equals method of its own, so my first guess would be that StringBuffer inherits the equals method of Object, which compares using sb1 == sb2. Therefore, both methods yield the same result.

Scharrels
  • 3,055
  • 25
  • 31
3

both compares two references to objects (sb1 is one, and sb2 is second), thus both are different.

If You are trying to compare content - use method compareTo(...) in String class - that is - first get String content of StringBuffer using method toString() (.toString().compareTo).

Ps. as of JDK 5, there is another much faster class that behaves exactly as StringBuffer - it is StringBuilder, and is also but is not thread safe.

StringBuffer sb1 = new StringBuffer("Java"); 
StringBuffer sb2 = new StringBuffer("Java"); 

System.out.println(sb1.toString().compareTo(sb2.toString())); 
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • StringBuilder is not thread safe. From the SDK docs on StringBuilder: "compatible with StringBuffer, but with **no guarantee of synchronization** ", from the introduction paragraph here: http://java.sun.com/j2se/1.5.0/docs/api/java/lang/StringBuilder.html – David Rodríguez - dribeas Jan 06 '10 at 11:26
  • of course, my bad. read the doc of StringBuffer too quick, IT is suplemented by StringBuilder, thus it is meant for use by a single thread. sorry for that. – mkolodziejski Jan 06 '10 at 11:32
  • The fact is that it is faster because it removes the thread safety requirement from StringBuffer – David Rodríguez - dribeas Jan 06 '10 at 11:45
  • Since JDK 1.6, lock elision makes the difference between `StringBuffer` and `StringBuilder` negligible. However, I don't know the details, maybe lock elision is not able to catch more complex use cases, so it's always better to use StringBuilder, if you don't need thread safety. – Oliv Jul 27 '16 at 06:47
1

Wondering why StringBuffer does not override the equals method. Probably because the content of the object is obtained by the toString() method and which has the desired method.

fastcodejava
  • 39,895
  • 28
  • 133
  • 186
1

With JDK/11, one can now compare two StringBuffers without an additional toString, this can be done using the newly introduced API -

public int compareTo​(StringBuffer another)

Compares two StringBuffer instances lexicographically. This method follows the same rules for lexicographical comparison as defined in the CharSequence.compare(this, another) method. For finer-grained, locale-sensitive String comparison, refer to Collator.

Implementation Note: This method synchronizes on this, the current object, but not StringBuffer another with which this StringBuffer is compared.

Returns: the value 0 if this StringBuffer contains the same character sequence as that of the argument StringBuffer; a negative integer if this StringBuffer is lexicographically less than the StringBuffer argument; or a positive integer if this StringBuffer is lexicographically greater than the StringBuffer argument.

Sample usage :

StringBuffer stringBuffer = new StringBuffer("null");
StringBuffer anotherStringBuffer = new StringBuffer("NULL");
System.out.println(stringBuffer.compareTo(anotherStringBuffer) == 0); // shall print 'false'
Naman
  • 27,789
  • 26
  • 218
  • 353
  • Posting this answer, since most of the questions pertaining to comparing the StringBuffer ends up here. – Naman Aug 10 '18 at 13:44
0

Stringbuffer's equals() is not overridden. It does not compare values, it only compares reference value assignments. That is why you are getting false as they both are referring to different objects.

Nikhil Arora
  • 329
  • 3
  • 9
0

That is tricky let me explain you in Object class equals method functionality is to match reference based on objects Sun developers had overridden the equals method in Collection, String, Wrapper classes so except this classes if you apply equals method anywhere it will check reference not content because indirectly every class in java is the child of Object class so if you want to check content in StringBuffer class you can override the object class equals method Thanks.

Saransh Dhyani
  • 397
  • 3
  • 9
-1

-> The String, StringBuffer and StringBuilder extends Object class. -> Object class contain equals() which compare two object memory address/ reference.
-> The equals() method is overridden in String class and it will check the

content.tostring()
System.out.println(sbuffer1.toString().equals(sbuffer2.toString()))

-> The equals() method of StringBuffer is not overridden from Object but implements Comparable, so it will compare reference equality, i.e., the same as using ==. in string class and equals() method in object.class. Thus, the natural ordering of StringBuffer is inconsistent with equals.

Vishal Sheth
  • 163
  • 1
  • 7