Playing with ==
and with pool of Strings
Java uses a mechanism called String interning , placing the Strings in pool to try to store only one copy of each character sequence in memory.
When Java finds string literals in code, it always returns a same instance of String, which points to an inner JVM in the pool . Therefore, it is quite possible to use the ==
operator to compare two variables that receive String literals:
String literal = "str";
String outraLiteral = "str";
System.out.println(literal == outraLiteral); //exibe true
In addition, as Java treats String literals as instances it is possible to compare a literal directly, like this:
System.out.println(literal == "str"); //também retorna true
On the other hand, we can not trust the comparison operator when we do not know how the String was created, since it is possible to create other instances in several ways. Example:
String novaInstancia = new String("str");
System.out.println("str" == novaInstancia); //retorna false
The above code creates a new instance of String, which is not the same as that returned by the JVM for the literal "str"
.
But, however, this does not mean that we have two entries of "str"
in the pool of Java. How can we verify this? Using the String.intern()
method, which returns a reference to the String that is in the pool . Example:
String novaInstancia = new String("str");
System.out.println("str" == novaInstancia.intern()); //retorna true
Applying this to the example question, we would have:
public class TesteString {
public static void main(String[] args) {
String str1 = "teste";
String str2 = "Oteste".substring(1);
System.out.println("str1: " + str1 + ", str2: " + str2);
if(str1 == str2.intern()) {
System.out.println("str1 igual a str2");
}
else {
System.out.println("str1 diferente de str2");
}
}
}
And the result:
str1: test, str2: test
str1 equal to str2
All very interesting. But what if we were to create a String in a way that was amazing?
StringBuilder sb = new StringBuilder();
sb.append('s');
sb.append('t');
sb.append('r');
System.out.println("str" == sb.toString().intern()); //continua sendo true
But what about equals()
?
If the comparison with ==
is faster than the equals()
method, should we abandon equals()
and use intern()
everywhere? The answer is no .
Not all Strings are internalized in pool immediately. When we call the intern()
method, if it is not there, then Java will add it. The problem is that once in the pool the String goes to permanent memory and will no longer be collected by the garbage collector .
When you want speed and the set of values is relatively small, using the intern()
method can be advantageous. But if we use this feature, for example, for processing text files, XML, databases, we will soon see OutOfMemoryError
.
In addition, adding a Strings in pool
can also be an "expensive" operation. In addition to checking to see if the String already exists, Java will probably have to handle competing accesses.
And finally, a big disadvantage is that the code gets more prone to bugs ( error prone ), since the developer must always put intern()
when needed.
Other ways of comparison
Going just beyond the exact comparison of Strings, we have other interesting ways of comparing:
Case insensitive (not case sensitive)
System.out.println("STR".equalsIgnoreCase("str")); //retorna true
A string contained in another
System.out.println("###STR###".contains("STR")); //retorna true
Which string is "bigger" than the other?
System.out.println("str1".compareTo("str2")); //retorna -1, pois "str1" é menor que "str2"
Or:
System.out.println("str1".compareToIgnoreCase("STR2")); //retorna -1, ignorando a capitalização
The compareTo
method returns:
-
1
if the first String is greater than the second
-
0
if they are equal
-
-1
if the first String is less than the second
Starts with ...
System.out.println("str1".startsWith("str")); //returna true, pois "str1" começa com "str"
Ends with ...
System.out.println("str1".endsWith("r1")); //return true, pois "str1" termina com "r1"
Regular expression
System.out.println("str2".matches("\w{3}\d")); //return true, pois corresponde à expressão regular
Is it empty?
String str1 = "";
System.out.println(str1.isEmpty());
System.out.println(str1.length() == 0);
System.out.println(str1.equals(""));
Particularly I prefer the first method for Java > = 6 and the second method for earlier versions.