ChatGPT解决这个技术问题 Extra ChatGPT

Is Java's assertEquals method reliable?

I know that == has some issues when comparing two Strings. It seems that String.equals() is a better approach. Well, I'm doing JUnit testing and my inclination is to use assertEquals(str1, str2). Is this a reliable way to assert two Strings contain the same content? I would use assertTrue(str1.equals(str2)), but then you don't get the benefit of seeing what the expected and actual values are on failure.

On a related note, does anyone have a link to a page or thread that plainly explains the problems with str1 == str2?

If you are unsure, you can read the code, or the Javadoc. BTW if you want to test they are the same object you can use assertSame.
If str1 and str2 are null, assertEquals() is true, but assertTrue(str1.equals(str2)) throws an exception. The first example will also print a useful error message such as the contents of str1 and str2, the second does not.

C
Community

You should always use .equals() when comparing Strings in Java.

JUnit calls the .equals() method to determine equality in the method assertEquals(Object o1, Object o2).

So, you are definitely safe using assertEquals(string1, string2). (Because Strings are Objects)

Here is a link to a great Stackoverflow question regarding some of the differences between == and .equals().


IIRC assertEquals() succeeds if both strings are null. If this is not what you want then call assertNotNull() as well.
additionally, if you want to test for ==, you can call assertSame()
I wouldn't say always; sometimes reference equality is desired, even for strings.
L
Laurence Gonsalves

assertEquals uses the equals method for comparison. There is a different assert, assertSame, which uses the == operator.

To understand why == shouldn't be used with strings you need to understand what == does: it does an identity check. That is, a == b checks to see if a and b refer to the same object. It is built into the language, and its behavior cannot be changed by different classes. The equals method, on the other hand, can be overridden by classes. While its default behavior (in the Object class) is to do an identity check using the == operator, many classes, including String, override it to instead do an "equivalence" check. In the case of String, instead of checking if a and b refer to the same object, a.equals(b) checks to see if the objects they refer to are both strings that contain exactly the same characters.

Analogy time: imagine that each String object is a piece of paper with something written on it. Let's say I have two pieces of paper with "Foo" written on them, and another with "Bar" written on it. If I take the first two pieces of paper and use == to compare them it will return false because it's essentially asking "are these the same piece of paper?". It doesn't need to even look at what's written on the paper. The fact that I'm giving it two pieces of paper (rather than the same one twice) means it will return false. If I use equals, however, the equals method will read the two pieces of paper and see that they say the same thing ("Foo"), and so it'll return true.

The bit that gets confusing with Strings is that the Java has a concept of "interning" Strings, and this is (effectively) automatically performed on any string literals in your code. This means that if you have two equivalent string literals in your code (even if they're in different classes) they'll actually both refer to the same String object. This makes the == operator return true more often than one might expect.


"That is, a == b checks to see if a and b are the same object." Technically it checks whether a and b REFER to the same object, since a and b are references. Unless I'm very wrong.
@user1903064 that is correct. Since non-primitive variables can only contain references in Java, it's common to skip the extra level of indirection when talking about them, but I agree that in this case being more explicit is beneficial. I've updated the answer. Thanks for the suggestion!
K
Ken Liu

In a nutshell - you can have two String objects that contain the same characters but are different objects (in different memory locations). The == operator checks to see that two references are pointing to the same object (memory location), but the equals() method checks if the characters are the same.

Usually you are interested in checking if two Strings contain the same characters, not whether they point to the same memory location.


C
Carl Manaster
public class StringEqualityTest extends TestCase {
    public void testEquality() throws Exception {
        String a = "abcde";
        String b = new String(a);
        assertTrue(a.equals(b));
        assertFalse(a == b);
        assertEquals(a, b);
    }
}

J
Jack Leow

The JUnit assertEquals(obj1, obj2) does indeed call obj1.equals(obj2).

There's also assertSame(obj1, obj2) which does obj1 == obj2 (i.e., verifies that obj1 and obj2 are referencing the same instance), which is what you're trying to avoid.

So you're fine.


S
Soviut

Yes, it is used all the time for testing. It is very likely that the testing framework uses .equals() for comparisons such as these.

Below is a link explaining the "string equality mistake". Essentially, strings in Java are objects, and when you compare object equality, typically they are compared based on memory address, and not by content. Because of this, two strings won't occupy the same address, even if their content is identical, so they won't match correctly, even though they look the same when printed.

http://blog.enrii.com/2006/03/15/java-string-equality-common-mistake/


j
jjnguy

"The == operator checks to see if two Objects are exactly the same Object."

http://leepoint.net/notes-java/data/strings/12stringcomparison.html

String is an Object in java, so it falls into that category of comparison rules.


This did not answer the question and is kind of misleading. You can't do == on a string reliably