(String) oder .toString ()?

89

Ich habe eine Methode mit einem Object o Parameter.

Bei dieser Methode weiß ich genau, dass es ein String"o" gibt, das nicht null ist. Es besteht keine Notwendigkeit, etwas zu überprüfen oder etwas anderes zu tun. Ich muss es genau wie ein behandelnString Objekt behandeln.

Nur neugierig - was ist billiger - es zu gießen Stringoder zu verwenden Object.toString()? Oder ist es nach Zeit- / CPU- / Mem-Preis gleich?

Update: Die Methode akzeptiert, Objectda es sich um die Implementierung einer Schnittstelle handelt. Es gibt keine Möglichkeit, den Parametertyp zu ändern.

Und es kann überhaupt nicht sein null. Ich wollte nur sagen, dass ich es nicht auf Null oder Leere prüfen muss. In meinem Fall gibt es immer eine nicht leere Zeichenfolge.

Vugluskr
quelle
1
In der .NET-Welt haben wir es gemessen und ToString () ist schneller. In Anbetracht des Grundes, warum dies so ist, gilt das Gleiche mit ziemlicher Sicherheit für eine jitting JVM.
Joshua

Antworten:

72

Das Casting in einen String ist billiger, da hierfür kein externer Funktionsaufruf erforderlich ist , sondern nur eine interne Typprüfung.

Euphorie83
quelle
4
Haben Sie es über mehrere JREs getestet? Ich habe überraschende Ergebnisse gesehen, zum Beispiel diese Situation in .NET. Realistisch bezweifle ich, dass die Leistung im wirklichen Leben von Bedeutung sein wird - aber das Casting ist aus defensiver Codierungssicht besser.
Jon Skeet
Der Methodenaufruf sollte inline sein. Die Verwendung von Generika zum Entfernen der (expliziten) Besetzung ist am besten.
Tom Hawtin - Tackline
@ Jon Skeet: Ich stimme zu, dass der Leistungsunterschied nicht viel sein wird. @ Tom Hawtin: Da der Typ des zu empfangenden Objekts zum Zeitpunkt der Kompilierung nicht bekannt ist, kann ich nicht sehen, wie der Methodenaufruf eingebunden werden kann. Können Sie bitte klarstellen?
Euphorie83
@ euphoria83: Eingefügt vom JIT-Compiler, nicht von javac.
Michael Myers
Nein, Methode kann eigentlich nicht inline sein. Der Typ ist nur als Objekt bekannt, und die tatsächliche Implementierung hängt vom Laufzeittyp ab. Welches schneller ist, hängt immer noch von der Implementierung ab, aber soweit ich mich erinnere (ich habe es tatsächlich einmal mit Microbenchmark getestet), scheint das Casting schneller zu sein. Dies ist keine offensichtliche Antwort: Die Typprüfung ist nicht immer schneller. Für den String-Typ kann es sein, dass es sich um ein Objekt (nicht um eine Schnittstelle) handelt, und zwar um das letzte.
StaxMan
45

Ich würde eine Besetzung verwenden. Das bestätigt Ihr "Wissen", dass es sich um eine Zeichenfolge handelt. Wenn Sie aus irgendeinem Grund einen Fehler haben und jemand etwas anderes als eine Zeichenfolge übergibt, ist es meiner Meinung nach besser, eine Ausnahme auszulösen (was eine Besetzung tun wird), als weiterhin mit fehlerhaften Daten auszuführen.

Jon Skeet
quelle
37

Laut Silly Performance Musings: x.toString () vs (String) x

Am Ende sind die Ergebnisse überraschend klar: Es ist mindestens doppelt so schnell, Object in String umzuwandeln, als Object.toString () aufzurufen.

Cletus
quelle
1
Ich habe diesen Code jetzt ausgeführt, der Unterschied ist vernachlässigbar (Mac, Java 8)
Stefan L
7

Wenn Sie wissen, dass das Objekt o ein String ist, würde ich sagen, dass Sie es einfach in einen String umwandeln und auf diese Weise erzwingen. Das Aufrufen von toString () für ein Objekt, von dem Sie sicher wissen, dass es sich um einen String handelt, kann zu Verwirrung führen.

Wenn Objekt o etwas anderes als ein String sein kann, müssen Sie toString () aufrufen.

Andy White
quelle
Das ist die richtige Antwort für mich. Warum? Weil das Casting (string)Registry.GetValue...eine Ausnahme für den Versuch auslöst, ein Int32-Objekt zu konvertieren, während es Registry.GetValue...ToString()wie erwartet funktioniert.
Schwerkraft
3

Die Leistung würde mich nicht allzu sehr beunruhigen, wenn diese Operation nur einige tausend Mal pro Sekunde durchgeführt würde - es gibt keinen spürbaren Unterschied.

Ich wäre jedoch besorgt darüber, die Eingabe zu "kennen". Sie haben eine Methode, die eine akzeptiert, Objectund Sie sollten sie als solche behandeln, dh Sie sollten nichts über den Parameter wissen , außer dass er an der ObjectSchnittstelle haftet , die zufällig eine toString()Methode hat. In diesem Fall würde ich dringend empfehlen, diese Methode zu verwenden, anstatt nur irgendetwas anzunehmen.

OTOH, wenn die Eingabe immer entweder Stringoder ist null, ändern Sie einfach die Methode, um Strings zu akzeptieren , und nullsuchen Sie explizit nach s (was Sie sowieso tun sollten, wenn Sie sich mit Nicht-Primitiven befassen ...)

Henrik Paul
quelle
Ich sagte, dass meine Frage keine wertvolle Bedeutung hat :) Ich bin nur neugierig, was theoretisch billiger ist. Aber danke trotzdem
Vugluskr
Die Kosten hängen davon ab, wie effizient die VM bei virtuellen Methodenaufrufen im Vergleich zur Typprüfung ist. Das ist implementierungsspezifisch.
Jon Skeet
2

Da der Referenztyp ein Objekt ist und alle Objekte einen toString () haben, rufen Sie einfach object.toString () auf. String.toString () gibt dies nur zurück.

  • toString () ist weniger Code zum Eingeben.
  • toString () ist weniger Bytecode.
  • Gießen ist eine teure Operation im Vergleich zu einem polymorphen Aufruf.
  • Die Besetzung könnte scheitern.
  • Verwenden Sie String.valueOf (Objekt), das nur object.toString () aufruft, wenn es nicht null ist.
mP.
quelle
1

Wenn das, was Sie in "o" haben, ein String ist, gibt es keinen großen Unterschied (wahrscheinlich ist die Umwandlung schneller, aber das ist eine VM / Library-Implementierungssache).

Wenn "o" möglicherweise kein String ist, es aber ein String sein soll, ist die Umwandlung genau das, was Sie wollen (aber Sie sollten die Methode veranlassen, einen String anstelle eines Objekts zu verwenden).

Wenn "o" ein beliebiger Typ sein könnte, müssen Sie den toString verwenden - aber achten Sie darauf, zuerst nach null zu suchen.

void foo(final Object o)
{
    final String str;

    // without this you would get a class cast exception
    // be wary of using instanceof though - it is usually the wrong thing to do
    if(o instanceof String)
    {
        str = (String)o;
    }    
}

oder

void foo(final Object o)
{
    final String str;

    // if you are 100% sure that o is not null then you can get rid of the else
    if(o != null)
    {
        str = o.toString();
    }
}

Ich würde den letzten lieber wie folgt codieren:

void foo(final Object o)
{
    final String str;

    if(o == null)
    {
        throw new IllegalArgumentException("o cannot be null");
    }

    str = o.toString();
}
TofuBeer
quelle
Die ersten beiden Snippets werden nicht wirklich kompiliert (die finalVariable wurde möglicherweise nicht initialisiert). Sie benötigen eine else, die entweder eine Ausnahme auslöst oder strzu etwas initialisiert .
Bruno Reis
1

Ich fand seltsamerweise, dass die Besetzung langsamer war als die vtable-Suche, die durch den tostring-Aufruf impliziert wurde.

Joshua
quelle
1

Es darf keine 'Nullzeichenfolge in o' geben. Wenn o null ist, enthält es keine Nullzeichenfolge, sondern nur null. Überprüfen Sie einfach zuerst o auf null. Wenn Sie werfen oder Anruf ToString () auf null werden Sie zum Absturz bringen.

Ed S.
quelle
2
Das Casting von null stürzt nicht ab. Es wird nicht einmal eine NullPointerException ausgelöst, sondern nur null für den neuen Typ aufgerufen. :)
Bombe