Results tagged “String”

And we're continuing with our next Java Tip. Back on using frameworks for our convenience, I present another often useful possibility to let the StringUtils handle lots of work for you.


Advice

Use Apache StringUtils for concatenating Strings from Arrays or Lists, also when you need to delimit them with an arbitrary string.

Code-Example

Before

...
SQL_WHERE += " AND V.cat_display_name IN (";
boolean addComma = false;
for (String category : filter.getCategories()) {
  if (addComma) {
    SQL_WHERE += ", ";
  }
  SQL_WHERE += "'" + category + "'";
  addComma = true;
}
SQL_WHERE += ") ";
...

...
String SQL = "";
for(String category: filter.getCategories()) {
    SQL += val + ", ";
}
SQL = SQL.substring(0, SQL.length()-2);
....

After

...         
SQL_WHERE += " AND V.cat_display_name IN (";
SQL_WHERE += "'" + StringUtils.join(filter.getCategories(), "', '") + "'";
SQL_WHERE += ") ";
...

Benefit

Huge readability gain and a safety gain. The code and intention is much clearer and the possibility to hide bugs in the loop-logic (which I have seen often) is not present. Also, depending on the length of the Array or List, it may also present a small performance gain (see Java Tip #2) but that's in most such cases neglibigle.

Remarks

None.

|

The next tip in my series of Java Tips deals with the usage of Java frameworks. Some people still have a certain hatred against Java frameworks because many of them have been not easy to deal with in in their past. Nevertheless, while those have either disappeared or evolved in a more usable shape, there have always been some frameworks which were rock-solid and widely accepted as simple and extremely useful. One of those is the Lang package in the library collection of the Apache Commons. In my opinion this one is a must-have for almost every Java project or application. Use it. Period.


Advice

Use Commons StringUtils for checking Strings content for null, emptiness or blankness.

Code-Example

Before

if(str != null && !str.equals("")) { ... }
if(str != null && !str.trim().equals("")) { ... }
if("".equals(str)) { ... }
if(str == null || "".equals(str.trim())) { ... }

After

if(StringUtils.isNotEmpty(str)) { ... }
if(StringUtils.isNotBlank(str)) { ... }
if(StringUtils.isEmpty(str)) { ... }
if(StringUtils.isBlank(str)) { ... }

Benefit

Huge readability gain. Safety gain, as StringUtils methods are null-safe. By using the provided utility methods from StringUtils the intent of a condition is much easier and faster to recognize. Furthermore, since all methods of StringUtils are able to correctly process null Strings, there is no danger of an unexpected NullPointerException anymore because of a forgotten null-check.

Remarks

None.

|

And on to the next in a series of Java Tips. While last time the performance gain was very tiny and only affected high-load scenarios, this tip may considerably speed up even simple applications where there is String-processing involved.


Advice

Use StringBuilder instead of + or += to concatenate Strings in non-linear cases (when concatenation not appears immediately one after another), ie. in loops.

Code-Example

Before

  String test = "";
  for(int i = 0; i < 50000; i++) {
     test += "abc";
  }

After

  StringBuilder test = new StringBuilder();
  for(int i = 0; i < 50000; i++) {
     test.append("abc");
  }

Benefit

Huge performance gain! The unoptimized code forces Java to create new Strings and copy the contents around all the time (because Strings are immutable in Java). The optimized code avoids this creation/copying by using StringBuilder. While the Java compiler can optimize linear concatenations, ie.

String test = "a" + "b" + "c";

into using a StringBuilder internally, it is not clever enough to apply this optimization correctly if there is more logic than just concatenation involved. Even if the concatenation is the only operation inside some loop-logic, the Java compiler falls back into creating a whole lot of String (or to be more exact: StringBuilder) objects, copying them around like crazy and causing a huge performance impact in some cases. I confirmed this example for measurement of StringBuilder performance: concatenating 50000 times "abc" in a loop takes ~11000ms, using StringBuilder keeps the time spent at near 0ms. (I tried concatenating with 500000 iterations, but stopped the execution of the first loop after ~10 minutes running unfinished, so the impact is nonlinear. Second loop finished in 15ms with 500000, for the record.)

Remarks

It is not necessary to optimize code like

String email = user + "@" + domain + ".com";

as javac is clever enough for these cases and it would reduce readability considerably. But even with the simplest loops involved the conditions change. For example, internally the first loop gets compiled by javac into following bytecode

 13  new java.lang.StringBuilder [24]
 16  dup
 17  aload_1 [test]
 18  invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [26]
 21  invokespecial java.lang.StringBuilder(java.lang.String) [32]
 24  ldc <String "abc"> [35]
 26  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
 29  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [41]
 32  astore_1 [test]
 33  iinc 4 1 [i]
 36  iload 4 [i]
 38  ldc <Integer 50000> [45]
 40  if_icmplt 13

where for each iteration of the loop(13-40) a new StringBuilder is instantiated and initialized with the result of the previous' loop StringBuilders value before appending the constant String just once each time. The optimized code results in this bytecode

 81  new java.lang.StringBuilder [24]
 84  dup
 85  invokespecial java.lang.StringBuilder() [62]
 88  astore 4 [builder]
 90  iconst_0
 91  istore 5 [i]
 93  goto 107
 96  aload 4 [builder]
 98  ldc <String "abc"> [35]
100  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
103  pop
104  iinc 5 1 [i]
107  iload 5 [i]
109  ldc <Integer 50000> [45]
111  if_icmplt 96

in which the loop(96-111) just appends the constant String to the same StringBuilder each time which was created only once before the loop started. No copying around and creation of additional objects necessary, thus a huge performance-gain.

| | Comments (2)

As promised last time I'm presenting here the first in a series of Java Tips. To start easy I'll begin with one of the simplest tip to implement.


Advice

Use StringBuilder instead of StringBuffer.

Code-Example

Before

  StringBuffer str = new StringBuffer();
  str.append("foo");
  String x = str.toString();

After

  StringBuilder str = new StringBuilder();
  str.append("foo");
  String x = str.toString();

Benefit

Small performance gain. StringBuilder is a 1:1 drop-in replacement for the StringBuffer class. The difference is that the StringBuilder is not thread synchronized and therefore performs better on most implementations of Java. Even javac internally translates String x = "foo" + "bar"; into a StringBuilder operation (confirmed with Java 1.6.0_18). A small testcase

public static void main(String[] args) {
    long start = System.currentTimeMillis();
    StringBuffer buffer = new StringBuffer();
    for(int i = 0; i < 1000000; i++) {
        buffer.append("abc");
    }
    System.out.println("Time lapse using StringBuffer: " + (System.currentTimeMillis() - start) + " milliseconds");

    start = System.currentTimeMillis();
    StringBuilder builder = new StringBuilder();
    for(int i = 0; i < 1000000; i++) {
        builder.append("abc");
    }
    System.out.println("Time lapse using StringBuilder: " + (System.currentTimeMillis() - start) + " milliseconds");
}

using Java 1.6.0_18 shows that StringBuilder is roughly 100% faster than StringBuffer. Although on my machine this testcase keeps below 100ms for both with one million appends so this won't be the magical solution for most of your performance issues.

Remarks

Even if this tip can be applied in 98% of all cases one has to be sure that the String is used afterwards and the StringBuilder instance does not get passed between several threads. Since StringBuilder is not thread-safe this would be the only case where StringBuffer should be used. And of course it's not the silver performance bullet but just one slice in string-heavy applications.

|

1

Archives