A senior Java programozók többségébe az évek tapasztalata mélyen beleégette az általános szabályt, hogy két String típusú változót ne a + operátorral fűzzünk össze, hanem a StringBuffer append metódusával. Ennek az alapja, hogy a String típus speciálisan kezelendő, ha két szöveget összefűzünk, akkor a memóriában három szövegünk lesz, ha ehhez hozzáfűzünk egy negyediket, akkor már öt: a másolgatás lassít és a GC is nehezményezi a többletmunkát. Az idők és a Java verziók változnak, érdemes kissé körüljárni a problémát. A StringBuilder előnyeit nem lehet elvitatni, egy rövid kis tesztprogrammal hamar be lehet bizonyítani, hogy a szövegek összefűzésére nem alkalmas a + operátor. Nézzük a programot:
Code Block |
---|
language | java |
---|
title | StringTest.java |
---|
linenumbers | true |
---|
|
public class StringTest
{
private static final int MAX=500000;
public long doString()
{
long start=System.currentTimeMillis();
String a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
for (int count=0;count<MAX;count++)
{
String aggregated="";
aggregated+=a;
// ... összesen 16 összefűzés
aggregated+=a;
}
return System.currentTimeMillis()-start;
}
public long doStringBuffer()
{
long start=System.currentTimeMillis();
String a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
for (int count=0;count<MAX;count++)
{
String aggregated="";
StringBuffer sb=new StringBuffer();
sb.append(a);
// ... összesen 16 összefűzés
sb.append(a);
aggregated=sb.toString();
}
return System.currentTimeMillis()-start;
}
public StringTest()
{
System.out.println("doString: "+doString()+"ms");
System.out.println("doStringBuffer: "+doStringBuffer()+"ms");
}
public static void main(String[] args)
{
new StringTest();
}
} |
A programot futtatva azt kapjuk, hogy a StringBuffer append metódusa ebben az esetben tízszer gyorsabb, mint a + operátor. Persze ezen lehet még gyorsítani, ha a StringBuffer megfelelő kezdő bufferméretet kap - a legjobb, ha ez pont egyezik az összefűzött szöveg méretével, ezért még biztosabban kijelenthetjük: mindig használjunk StringBuffer-t. Módosítsuk kissé a teszteléshez szükséges programot az alábbiak szerint:
Code Block |
---|
language | java |
---|
linenumbers | true |
---|
|
public long doString()
{
long start=System.currentTimeMillis();
String a="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
for (int count=0;count<MAX;count++)
{
String aggregated=a+a+a+a+a+a+a+a+a+a+a+a+a+a+a+a;
}
return System.currentTimeMillis()-start;
} |
Mint látható, egyszerűen annyit csináltunk, hogy egymás mögé vontuk össze a szövegek összefűzését, s meglepő módon a két metódus közel azonos idő alatt futott le... feltéve, hogy Java 1.5 futtatókörnyezetre fordítottuk, ez alatt ugyanis megmarad a tízszeres különbség, ezzel egyező vagy nagyobb verziószámú célra fordítva azonban a szöveget összefűzése is gyors lesz. Mi lehet az oka? Nézzük csak meg a fordítot .class fájlt egy Java Decompiler programmal.
Code Block |
---|
language | java |
---|
title | Java 1.4 fordítás |
---|
linenumbers | true |
---|
|
public long doString()
{
long l = System.currentTimeMillis();
String str1 = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
for (int i = 0; i < 500000; ++i)
{
String str2 = str1 + str1 + str1 + str1 + str1 + str1 + str1 + str1 +
str1 + str1 + str1 + str1 + str1 + str1 + str1 + str1;
}
return (System.currentTimeMillis() - l);
} |
Code Block |
---|
language | java |
---|
title | Java 1.5 fordítás |
---|
linenumbers | true |
---|
|
public long doString()
{
long l = System.currentTimeMillis();
String s = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
for(int i = 0; i < 0x7a120; i++)
{
String s1 = (new StringBuilder()).append(s).append(s).append(s).append(s).
append(s).append(s).append(s).append(s).
append(s).append(s).append(s).append(s).
append(s).append(s).append(s).append(s).toString();
}
return System.currentTimeMillis() - l;
} |
Mint látni, a fordító kicserélgeti magától a szövegek összefűzését StringBuilder összefűzésre, és ezáltal pont olyan gyors lesz, mintha a StringBuilder append metódusát használnák, mivel pontosan arra fordul, amit a másik metódusban írtunk.
A fentiek fényében, ha egy - a fordító számára összefüggő - sorban használjuk a + operátort, akkor a fordító ezt a kódba már append hívássá fogja fordítani. Az eredmény olvashatóbb forráskód és azonos teljesítmény.