…ale některé věci můžem dělat lépe. Při sledování
jednoho výborného videa Java Puzzles jsem si uvědomil, jak málo toho
vím a jak bez větší námahy můžem napsat aspoň trošku
efektivnější kód. Proto jsem se začal prohrabával
kódem mým i kolegů a narazil jsem na 3 často se
opakující věci (chybky) u často používaného kódu.
Spojování řetězců
V kódu jsem objevil, že se používají 3 způsoby, jak
spojit řetězec, viz demonstrační metody:
public String str1(int value) {
builder.append(„String : “ + value + “ neco“);
builder.append(„String : “ + value + “ neco“);
builder.append(„String : “ + value + “ neco“);
builder.append(„String : “ + value + “ neco“);
return builder.toString();
}
public String str2(int value) {
StringBuilder builder = new StringBuilder();
builder.append(„String :
„).append(value).append(“ neco“);
builder.append(„String :
„).append(value).append(“ neco“);
builder.append(„String :
„).append(value).append(“ neco“);
builder.append(„String :
„).append(value).append(“ neco“);
return builder.toString();
}
public String str3(int value) {
String str =
„String : “ + value + “ neco“
+ „String : “ + value + “ neco“
+ „String : “ + value + “ neco“
+ „String : “ + value + “ něco“;
return str;
}
Pří dostatečném množství opakování 1 000 000
dostaneme tyto hodnoty:
- str1 1406 ms
- str2: 633 ms
- str3: 697 ms
Z naměřených hodnot vidíme, že metoda str1 je cca
2.2x pomalejší než další 2 metody. A důvodem je, že
compilátor převádí retězení Stringu pomocí ‚+‘ na
volání StringBuilder.append(). Takže když se podíváme
na první metodu str1() tak vlastně vytváříme 4x
instanci třídy StringBuilder. Detail si můžete
prohlidnout v příloze přímo na výpisu bytecode, získaný utilitkou javap, která
je v JDK.
Java primitives X Wrapper types
Java není čistě OOP jazyk, právě protože má
primitivní datové typy jako je int, boolean.
Samozřejmně existují jejich objektové reprezentace
definované v java.lang, např. Java.lang.Integer apod.
Java. Díky tomu, že Java provádí autoboxing
můžeme pracovat s objektovými
reprezentacemi jako by to byl primitivní datový typ.
Ale autoboxing, něco stojí, kolik ukazuje tyto dvě
jednoduché metody:
public Integer secti(Integer i1, Integer i2) {
return i1 + i2;
}
public int secti2(int i1, int i2) {
return i1 + i2;
}
Jak daný autobxing vypadá ukazuje výpis bytecodu
public java.lang.Integer secti(java.lang.Integer,
java.lang.Integer);
Code:
0: aload_1
1: invokevirtual #9; //Method
java/lang/Integer.intValue:()I
4: aload_2
5: invokevirtual #9; //Method
java/lang/Integer.intValue:()I
8: iadd
9: invokestatic #10; //Method
java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
12: areturn
public int secti2(int, int);
Code:
0: iload_1
1: iload_2
2: iadd
3: ireturn
Po provedení testu, kdy jsem jednotlivé metody
volal v cyklu 1 000 000x výsledky jednotivých metod:
- secti: 117 ms
- secti2: 0 ms
java.util.Vector x
java.util.ArrayList
Na mnoha místech, zejména v kódech 10 let starém se
používá Vector místo ArrayList po malé diskuzi s
kolegy byly i takový(pracovně déle sloužící), kteří
neví, jaký je rozdíl mezi těmito kolekcemi. Hlavním a
největším rozdílem je, že všechny kolekce, které byly
přidány v rámci Java 1.2 a později nejsou synchronized. Hastable x HashMap, Stack x
LinkedList. Protože stejně se snima v 95% pracuje v
rámci jednoho vlákna a pokud je potřeba přístup do
kolekce v rámci více vláken, existuje utils třída
java.util.Collections a její statické metody
synchronizedXXX, které vytvoří synchronized kolekci.
Rozdíl v časech mezi třídami Vector a ArrayList na
nejvíce používaných operací tj. Metoda add
a iterace přes Iterator. Uvedené časy jsou
po 20 opakování nad kolekcemi o 100 000 objektů. Celý
test viz TestList.java.
- add Vector: 8.05 ma
- add ArrayList: 3.4 ms
- iterator Vector: 14.7 ms
- iterator ArrayList: 3.95 ms
Shrnutí
Tyto věci neurychlí aplikaci zázračním způsobem,
ale jak se říká „kupka ke kupce a je z toho
hromada„. A pokud budete mít aplikaci špatně
navrženou a napsanou, tak budete mít asi větší
starosti jak jí vůbec udržovat a rozvíjet, než si
hrát s takovýma drobnostma.
Když už přetěžujete metodu toString(), zejména u
domain objektů pro lepší logování, vyhněte se prvnímu
spůsobu, i když je to taková opičí práce nebuďtě
líní. A ještě lepší řešení, nechte si metodu
vygenerovat. V Netbeans to jde například po zmáčknutí
alt+Insert.
Používejte primitivní datové
typy kde to jen jde, protože s
autoboxingem je spojeno i hodně úskalí, např. musíte
kontrolovat hodnotu na null a to také znamená, že
java.util.Boolean nemá jen true a false, ale má 3
stavy true, false a null.
Další pěknou ukázkou jen si zkuste typnout co
vypíše tučně označený kód 🙂
Integer b = 128;
System.out.println („a >= b : “ +
(a>=b));
System.out.println („a > b : “ +
(a>b));
System.out.println („a == b : “ +
(a==b));
System.out.println („—————-„);
Integer x = -128;
Integer y = -128;
System.out.println („x >= y : “ +
(x>=y));
System.out.println („x > y : “ +
(x>y));
(x==y));
bych nějaké další příklady, které se třeba vyskytují
na vašich projektech.
Seznam příloh:
- TestList.java, obsahuje
test pro test Vector x ArrayList -
TestVykon.java
ukazuje rozdíl v řetězení stringů a autoboxing. - bytecode obsahuje bytecode pro TestVykon.java