Introduction
While beginning development in Java, especially if coming from a .NET background (but not necessarily), you might do string
comparison with ==
in Java. Don’t do it. It will compare the string
instances and not their effective value. You might even try it first to check if ==
really works, testing it in a wrong manner like so:
public static void main(String[] args) {
String s1 = "Abc";
String s2 = "Abc";
System.out.println("s1 == s2 -> " + (s1 == s2));
}
This will output:
s1 == s2 -> true
.. which might lead you to believe this works. This does return the correct value because of a feature present in Java and .NET called string interning (not specific to Java or .NET).
Try to obtain a string
instance dynamically like concatenating two existing instances and see how things don’t work anymore:
public static void main(String[] args) {
String s1 = "Abc";
String s2 = "Abc";
String capitalA = "A";
String bc = "bc";
String s3 = capitalA + bc;
System.out.println("s1 == s2 -> " + (s1 == s2));
System.out.println("s1 == s3 -> " + (s1 == s3));
}
s1 == s2 -> true
s1 == s3 -> false
Weird, huh? That’s because at compile time, there are four distinct string
s generated: “Abc
” (once, even if referred twice), “A
” and “bc
”. The “Abc
” instance obtained by joining “A
” and “bc
” will be generated at runtime and, of course, it will be a different instance than the first “Abc
” instance. That’s why the result of the ==
operator comparison will be false
.
But the compiler can be smarter than you might expect. How’s that? Here’s how:
public static void main(String[] args) {
String s1 = "Abc";
String s2 = "Abc";
String capitalA = "A";
String bc = "bc";
String s3 = capitalA + bc;
String s4 = "A" + "bc";
System.out.println("s1 == s2 -> " + (s1 == s2));
System.out.println("s1 == s3 -> " + (s1 == s3));
System.out.println("s1 == s4 -> " + (s1 == s4));
}
.. resulting in:
s1 == s2 -> true
s1 == s3 -> false
s1 == s4 -> true
The compiler inferred the result of the string
concatenation in the s4
initialization and then interned the resulted string
, detected the already existing “Abc
” instance and reused it.
Alright, that’s not the right way to do it, the correct way to do it is to use the .equals(String s)
instance method of the String
class. Like so:
public static void main(String[] args) {
String s1 = "Abc";
String capitalA = "A";
String bc = "bc";
String s2 = capitalA + bc;
System.out.println("s1.equals(s2) -> " + s1.equals(s2));
}
This works nice:
s1.equals(s2) -> true
All’s well until we tweak things a bit:
public static void main(String[] args) {
String s1 = null;
String s2 = null;
System.out.println("s1.equals(s2) -> " + s1.equals(s2));
}
Guess what, it’s not “true” that’s being displayed but the stack trace of a NullPointerException
. Your solution might be some weird ternary expression (yuck) that looks like so:
public static void main(String[] args) {
String s1 = null;
String s2 = null;
boolean b = s1 == null ? s2 == s1 : s1.equals(s2);
System.out.println(b);
}
You can extract this expression into a static
method in a helper class if you find yourself doing this often.
As a final aspect to consider, if one of the two string
s is a constant value, you can still use equals
on that one like so:
public static void main(String[] args) {
String s1 = getData();
String s2 = "someValue";
System.out.println(s2.equals(s1));
System.out.println("someOtherValue".equals(s1));
}