Hi,
Nein! Wieso? Will er den String ändern? Nein. Wieso sollte er dann StringBuffer verwenden?
Aus Performancegründen (wie ich begründet habe) - Beispiel:
import java.util.Vector;
/** * @version 0.1 * * @history 17.01.2003 Martin Jung created * * @author <a href="mailto:junmar@web.de">Martin Jung</a> */ public class StringPerformance {
static int length = 100000; static String[] arr = new String[length]; static String result = ""; static long begin, lap; static String NL = System.getProperty("line.separator");
static { for (int i = 0; i < arr.length; i++) { arr[i] = new String("a"); // assure to have disctinct String objects if (i > 0) { if (arr[0] == arr[i]) { System.out.println("Exit because no distinct String object" + " at position:" +i); System.exit(1); } } } System.out.println("created object of type " +StringPerformance.class+ " with " +length+ " String objects" +NL); }
public static void main(String[] arguments) { concat(); stringBuffer(); cast(); instanceOf(); }
public static void concat() {
System.out.println("entering concat()..."); // for-Schleife entfaltet result = arr[0] .concat(arr[1]) // 1. temporäres String Object .concat(arr[2]) // 2. temporäres String Object .concat(arr[3]); /* das bedeutet: für n Konkatenierungen mit .concat() werden (unnötigerweise) n-1 temporäre String Objekte kreiert, die dann wieder 'GC'-ed werden (müssen)! Darunter dürfte sowohl die Performance leiden als auch das Speicherprofil */
// test begin = System.currentTimeMillis(); for (int i = 0; i < arr.length; i++) { result = result.concat(arr[i]); } lap = System.currentTimeMillis() - begin; System.out.println("concat(): " +lap+ " msec" +NL); }
public static void stringBuffer() {
System.out.println("entering stringBuffer()..."); StringBuffer buf = new StringBuffer();
// test begin = System.currentTimeMillis(); for (int i = 0; i < arr.length; i++) { buf = buf.append(arr[i]); } result = buf.toString(); lap = System.currentTimeMillis() - begin; System.out.println("stringBuffer(): " +lap+ " msec" +NL); }
public static void cast() {
System.out.println("entering cast()"); Vector v = new Vector(); v.add("Hubbabubba"); int max = length * 100; String s; System.out.println("now using " +max+ " String objects...");
begin = System.currentTimeMillis(); for (int i = 0; i < max; i++) { s = v.get(0).toString(); } lap = System.currentTimeMillis() - begin; System.out.println("toString: " +lap+ " msec");
begin = System.currentTimeMillis(); for (int i = 0; i < max; i++) { s = (String)v.get(0); } lap = System.currentTimeMillis() - begin; System.out.println("cast: " +lap+ " msec" +NL); /* Du hast Recht (mit JDK 1.3.1) - allerdings dürfte dieser * Performanceeffekt besonders von der VM-Implementierung abhängen. */ }
public static void instanceOf() {
System.out.println("entering instanceOf()"); Vector v = new Vector(); v.add("Hubbabubba"); int max = length * 100; String s; System.out.println("now using " +max+ " String objects...");
begin = System.currentTimeMillis(); for (int i = 0; i < max; i++) { s = (String)v.get(0); } lap = System.currentTimeMillis() - begin; System.out.println("cast ohne type check: " +lap+ " msec");
Object obj; begin = System.currentTimeMillis(); for (int i = 0; i < max; i++) { obj = v.get(0); if (obj instanceof String) { s = (String) obj; } } lap = System.currentTimeMillis() - begin; System.out.println("cast mit type check: " +lap+ " msec"); /* Du hast Recht (mit JDK 1.3.1) - allerdings dürfte dieser * Performanceeffekt besonders von der VM-Implementierung abhängen. */ } }
Nein, man sollte das ganz sicher nicht so tun! Was ist, wenn er irgendwo einen Programmierfehler hat und nun statt einem String weißnicht ein Integer-Objekt in den Vector geschrieben hat. Ein toString() würde für den Integer funktionieren.
Hannes deklarierte: private Vector fErrorLog = new Vector();
Daraus schloss ich spekulativ, der Programmierer beabsichtigt dem Vektor werden ausschließlich String Objekte hinzuzufügen und macht dies auch (der 'private' access modifier ließ mich zusätzlich vermuten, der Vektor stellt ein internes Member dar).
Bei einem Cast würde - wie gewünscht - eine ClassCastException geworfen.
Wieso ist das erwünscht? Ob eine ClassCastException geworfen werden soll, ist eine Designentscheidung. Diese würde ich variabel treffen:
Entwicklung: Ein expliziter Cast hilft, um Programmierfehler aufzudecken. Sollte es sich weiterhin um einen gänzlich privaten Member halten (ohne Getter/Setter), wäre der cast - wie von Dir gezeigt - die performantere Lösung (zumindest mit JDK 1.3.1). Anmmerkung: wenn der (private) Vector hingegen über eine publizierte API (Getter/Setter) zugänglich gemacht wird/werden soll, ist der cast zwar immer noch performanter, verhindert aber nicht, dass irgendjemand folgendes in seinem Code folgendes schreibt:
BesagtesObjekt obj = new BesagterObjekt(); Vector v = obj.gibMirErrorLogVector(); v.add(Integer.toString(5));
- Produktivversion: Sagen wir spekulativ, der Code-Ausschnitt ist Teil einer Logging-Funktionalität. Unabhängig, ob das Logging 'nur' für Debug-Monitoring verwendet wird oder Teil der dem Benutzer angebotenen Programmfunktionalität werden/sein soll (z.B. ein Log bei einem Backup-Programm) würde ich definitiv keine ClassCastException zulassen. Warum: a) eine RuntimeException führt zum Programmabbruch (- sofern sie nicht abgefangen wird) b) Das unbeabsichtigte Loggen von Informationen (irgendwelchen String-Objekten) rechtfertigt in keinem Falle einen Programmabbruch. c) Aufgrund der Performancetests würde ich dieses dann durch einen cast mit vorangegangener Typprüfung gewährleisten.
Du erkaufst dir mit deinem toString() eine schwer auffindbare Fehlerquelle!
Warum schwer auffindbar? Ich sollte die Stelle meines Codes kennen, an dem die Log-Infos assembliert werden. Also weiß ich, wo ich einen Breakpunkt zu setzen habe, starte den Debugger und sollte die Fehlerursache (relativ - diese Einschränkung gilt beim Entwanzen ja prinzipiell ;-)) ) schnell gefunden haben.
Übrigens ist die toString()-Version sogar langsamer als die Version mit dem Cast, wie folgender Code beweist:
Du hast Recht - siehe Beispielcode. Ich denke allerdings, dass dieser Performanceunterschied von der Implementierung der VM abhängt (während der Performanceunterschied wzischen String.concat() und StringBuffer.append() primär durch die Spezifikation begründet ist - wobei eine VM-Implementierung natürlich genau diesbezüglich besonders optimiert sein könnte)
Viele Grüße, Martin Jung