Tuesday, September 25, 2007

Java: Few important points when it comes to Strings

How many times have you coded a check for String being null or empty? Countless times, right? I have. We use some ready-to-use classes from open source frameworks or we write our own StringUtils class. More or less they all implement the same thing and it always looks similar to the following code snippet:

String s = ...
if (s == null || s.equals(""))...

or similar to the following, which trims leading and ending whitespaces

String s = ...
if (s == null || s.trim().equals(""))...

Of course you could also do this:

"".equals(s)

which is a case when you do not care if String s is null and you don't have to worry about NPE as if won't happen ("" is never null, whereas s could be). But that's another story.

I have had "extra" warnings turned on in my IDE for couple of days. But today my IDE suprised me when it highlighted

[1] s.equals("")

and suggested that I could optimize it by making it to

[2] s.length() == 0

And guess what?! The IDE was right! I looked at the suggested code briefly, gave it a bit of thought and agreed that it would probably be faster. Method [1] creates a new instance of the String (an empty String, yes I know that all instances of "" would be caught during compilation and optimized and that they all would refer to the same instance). Just to be on the safe side I looked at the source of the String class.

And here is what I found. The length() method returns and integer primitive, which is not calculated with each method call to length(). It is rather a member variable (or constant, as Strings are invariants) of String class that is calculated when new String instance is created. So this method would be super fast.

public int length() {
return count;
}

On the other side, there is the equals() method, which is fast as well, but not as fast as length method. It has to do a check for class, class casting and comparison of count members (that's what length method returns).

public boolean equals(Object anObject) {
if (! (anObject instanceof String))
return false;
String str2 = (String) anObject;
if (count != str2.count)
return false;
if (value == str2.value && offset == str2.offset)
return true;
int i = count;
int x = offset;
int y = str2.offset;
while (--i >= 0)
if (value[x++] != str2.value[y++])
return false;
return true;
}

And remember the few important points when it comes to Strings:

  • Do not compare Strings with == operator. Unless you want to compare the object references. Use equals() method.
  • Do not construct new instances like new String("abc"). Simple "abc" will do, unless you really mean that you need a new instance of String with same value.
  • Do not concatenate Strings in loops using + operator. It's faster to use StringBuffer (or StringBuilder, which is in Tiger and is not synchronized) append() and then toString() methods instead. The plus (+) operator constructs new String object each time.

Source: hanuska.blogspot.com

8 Comments:

Unknown said...

Thanks for choosing to quote my blog post Empty String!

Follow these links for more articles about Java (String) or more.

IT Efforts said...

Thank you for interesting blog about Java and other things. Very much useful information. I collect and publish notes interesting to me on this blog. I think you will not be against.
Good luck.

Unknown said...

As we speak of comparing Strings and all those .equals("") I would like to tip about all the convenient Utils classes in Apache Commons. (Like StringUtils.isEmpty(string) etc..

Jeremy Weiskotten said...

Instead of testing for string.length() == 0, you can call string.isEmpty(). It conveys the intent better.

Unknown said...

on the same subject:

[1] http://java.sun.com/developer/JDCTechTips/2002/tt0305.html#tip1
[2] http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#39990

afsina said...

i think it is ok (even preferable for clarity) to use + sign if used moderately for small amount of concatenations.

Unknown said...

In my humble experience, you teach the novice developer how to use StringBuilder/StringBuffer, and the next thing you see is:

buffer.append("foo " + bar + "baz?");

Instead of

buffer.append("foo ").append(bar).append("baz?");

Then you tell him about this. Next time you look, they are using StringBuilder everywhere when simple String concatenation would work.

Snif...

Unknown said...

It's not true that the + operator creates a string each time. The compiler actually creates a StringBuffer/Builder in the background and appends all strings together, when possible.

Take a look at my post about the subject.