(String) czy .toString ()?

89

Mam metodę z Object oparametrem.

W tej metodzie dokładnie wiem, że istnieje String„o”, które nie jest zerowe. Nie ma potrzeby sprawdzać ani robić czegoś innego. Muszę traktować to dokładnie jak Stringprzedmiot.

Po prostu ciekawy - co jest tańsze - rzuć to Stringlub użyj Object.toString()? A może to samo dotyczy ceny czasu / procesora / pamięci?

Aktualizacja: metoda akceptuje, Objectponieważ jest to implementacja interfejsu. Nie ma możliwości zmiany typu parametru.

I wcale tak nie może być null. Chciałem tylko powiedzieć, że nie muszę go sprawdzać pod kątem wartości zerowej lub pustej. W moim przypadku zawsze jest niepusty ciąg.

Vugluskr
źródło
1
W świecie .NET zmierzyliśmy to i ToString () jest szybsze. Biorąc pod uwagę powód, dla którego tak się dzieje, prawie na pewno to samo dotyczy jitting JVM.
Joshua

Odpowiedzi:

73

rzutowanie na String jest tańsze, ponieważ nie wymaga zewnętrznego wywołania funkcji, a jedynie wewnętrzne sprawdzanie typu.

euforia83
źródło
4
Czy przetestowałeś to w wielu środowiskach JRE? Widziałem zaskakujące wyniki na przykład w tej sytuacji w .NET. Realistycznie wątpię, czy wydajność będzie miała znaczenie w prawdziwym życiu - ale casting jest lepszy z perspektywy kodowania obronnego.
Jon Skeet
Wywołanie metody powinno zostać wbudowane. Najlepszym rozwiązaniem byłoby użycie typów generycznych do usunięcia (jawnego) rzutowania.
Tom Hawtin - tackline
@Jon Skeet: Zgadzam się, że różnica w wydajności nie będzie duża. @Tom Hawtin: Ponieważ typ obiektu, który zostanie odebrany, nie jest znany w czasie kompilacji, nie widzę, jak wywołanie metody może zostać wbudowane. Czy możesz wyjaśnić proszę?
euphoria83
@ euphoria83: Wbudowane przez kompilator JIT, a nie przez javac.
Michael Myers
Właściwie nie, metody nie można wstawić. Typ jest znany tylko jako Object, a rzeczywista implementacja zależy od typu środowiska uruchomieniowego. Który z nich jest szybszy, nadal zależy od implementacji, ale o ile pamiętam (w pewnym momencie testowałem go z microbenchmark), rzutowanie wydaje się być szybsze. Nie jest to jednak oczywista odpowiedź: sprawdzanie typów nie zawsze jest szybsze. W przypadku typu String może to być, ponieważ jest to obiekt (nie interfejs) i na tym koniec.
StaxMan
45

Użyłbym odlewu. To potwierdza twoją „wiedzę”, że jest to ciąg. Jeśli z jakiegoś powodu skończysz z błędem i ktoś przekaże coś innego niż string, myślę, że lepiej byłoby zgłosić wyjątek (co zrobi rzutowanie) niż kontynuować wykonywanie z błędnymi danymi.

Jon Skeet
źródło
7

Jeśli wiesz, że Object o jest Stringiem, powiedziałbym, że po prostu rzuć go na String i wymuszaj to w ten sposób. Wywołanie metody toString () na obiekcie, o którym wiesz na pewno, że jest ciągiem, może tylko wprowadzić zamieszanie.

Jeśli Object o może być czymś innym niż String, musisz wywołać toString ().

Andy White
źródło
To jest dla mnie poprawna odpowiedź. Czemu? Ponieważ rzutowanie (string)Registry.GetValue...zgłasza wyjątek podczas próby rzutowania obiektu Int32, podczas gdy Registry.GetValue...ToString()działa zgodnie z oczekiwaniami.
grawitacja
3

Nie przejmowałbym się zbytnio wykonaniem, gdyby ta operacja była wykonywana nawet kilka tysięcy razy na sekundę - nie ma żadnej namacalnej różnicy.

Obawiałbym się jednak „znajomości” danych wejściowych. Masz metodę, która akceptuje an Objecti powinieneś ją tak traktować, tj. Nie powinieneś nic wiedzieć o parametrze, poza tym, że jest on zgodny z Objectinterfejsem, który tak się składa, że ​​ma toString()metodę. W tym przypadku zdecydowanie sugerowałbym użycie tej metody zamiast po prostu zakładać cokolwiek.

OTOH, jeśli dane wejściowe to zawsze albo Stringalbo null, po prostu zmień metodę na akceptację Strings i sprawdź jawnie nulls (co i tak powinieneś zrobić, gdy masz do czynienia z nieprymitywami ...)

Henrik Paul
źródło
Powiedziałem, że moje pytanie nie ma wartościowego znaczenia :) Jestem po prostu ciekawy, co jest teoretycznie tańsze. Ale dzięki temu
Vugluskr
Koszt będzie zależał od wydajności maszyny wirtualnej przy wywoływaniu metod wirtualnych w porównaniu ze sprawdzaniem typu. To jest specyficzne dla implementacji.
Jon Skeet,
2

Zakładając, że typem referencyjnym jest Object, a wszystkie obiekty mają toString (), po prostu wywołaj object.toString (). String.toString () po prostu zwraca to.

  • toString () to mniej kodu do wpisania.
  • toString () to mniejszy kod bajtowy.
  • odlewanie jest kosztowną operacją VS wywołanie polimorficzne.
  • obsada może zawieść.
  • Użyj String.valueOf (object), który po prostu wywołuje object.toString (), jeśli nie ma wartości null.
poseł.
źródło
1

Jeśli to, co masz w "o", jest ciągiem, nie ma dużej różnicy (prawdopodobnie rzutowanie jest szybsze, ale jest to kwestia implementacji VM / biblioteki).

Jeśli „o” może nie być Stringiem, ale przypuszczalnie jest to String, to rzutowanie jest tym, czego chcesz (ale powinieneś sprawić, by metoda przyjmowała String zamiast Object).

Jeśli "o" może być dowolnego typu, musisz użyć toString - ale najpierw sprawdź, czy nie ma null.

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;
    }    
}

lub

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();
    }
}

Wolę zakodować ostatni jako:

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

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

    str = o.toString();
}
TofuBeer
źródło
Pierwsze 2 fragmenty tak naprawdę się nie kompilują ( finalzmienna mogła nie zostać zainicjowana). Potrzebujesz elementu else, który wyrzuci wyjątek lub zainicjuje strcoś.
Bruno Reis
1

Zauważyłem dziwnie, że rzutowanie było wolniejsze niż wyszukiwanie tabeli vtable sugerowane przez wywołanie tostring.

Joshua
źródło
1

W o nie może być „łańcucha zerowego”. Jeśli o jest null, nie zawiera łańcucha zerowego, jest po prostu zerowe. Po prostu sprawdź najpierw o zero. Jeśli rzucisz lub wywołasz ToString () na null, nastąpi awaria.

Ed S.
źródło
2
Przesyłanie wartości null nie ulegnie awarii. Nie zgłosi nawet wyjątku NullPointerException, po prostu wywoła null do nowego typu. :)
Bombe