...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 a = 128;
     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));
    System.out.println ("x == y : " + (x==y));
 
 Pokuď se dostanete při čtení až semka uvítal bych nějaké další příklady, které se třeba vyskytují na vašich projektech.

Seznam příloh: