2013-04-23 9 views
19

W mojej aplikacji obj.getClass().isArray() jest nazywany bardzo często i staje się wąskim gardłem aplikacji.
Chcę sprawdzić efektywnie w czasie wykonywania, jeśli obiekt jest tablicą. Pierwotna tablica i tablica obiektów powinny zwracać wartość true.
Sposób, w jaki mogę sobie wyobrazić, to instanceof wszystkie pierwotne tablice, ale nie radzą sobie z typami takimi jak int [] []. Aplikacja jest używana jako lib, więc nie mogę wyświetlić wszystkich typów.
Czy są na to jakieś wskazówki?Object.isArray() jest powolny, czy istnieje szybki sposób na zrobienie tego?

+2

To nie odpowiada na twoje pytanie, ale jest miłym dodatkiem do dyskusji: http://stackoverflow.com/questions/219881/java-array-reflection-isarray-vs-instanceof –

+10

Myślę, że pytanie brzmi: dlaczego często trzeba wywoływać tę metodę? Mam nadzieję, że gdyby było szybsze, programiści JDK oprą na nim swoją implementację. Jeśli nie, musisz podać więcej szczegółów na temat rzeczywistego problemu, który próbujesz rozwiązać. – Axel

+3

Twoje rozumowanie "instanceof" nie jest całkowicie poprawne. 'int [] [] instanceof Object []' daje 'true' (tak jak' int [] instanceof Object'), więc te przypadki są objęte ... Nie jestem pewien czy jest to istotne dla twojej rzeczywistej domeny problemu (zobacz Komentarz Axela). –

Odpowiedz

8

Poziom odniesienia Właśnie odbywa dał następujące wyniki:

{s instanceof Object[]} spends 44ms 
{s.getClass().getName().charAt(0) == '['} spends 58ms 
{s.getClass().isArray()} spends 303ms 

Benchmark zostało zrobione przy użyciu Benchmark.java, zwane z Main.java.


Po omówiono zastosowanie zmiennej final w powyższym teście, zobaczyć nowe wyniki z użyciem lokalna:

{s instanceof Object[]} spends 83ms 
{s.getClass().getName().charAt(0) == '['} spends 93ms 
{s.getClass().isArray()} spends 354ms 

Nawet jeśli okresy są nieco dłuższe (ciekawe btw) , ich kolejność została zachowana.

Benchmark.java został nazwany tym nowym Main.java.


i stosując prymitywną tablicę o nazwie z tej drugiej Main.java:

{a instanceof int[]} spends 71ms 
{a.getClass().getName().charAt(0) == '['} spends 82ms 
{a.getClass().isArray()} spends 340ms 

Nadal takie same wyniki zamówienie.

+0

Czy umieścisz kod testu porównawczego? Dostałem [przeciwne wyniki] (http://stackoverflow.com/a/16171137/521799) –

+0

@LukasEder [Benchmark.java] (http://pastebin.com/pskJ1tSy) i [Main.java] (http: //pastebin.com/KbptdWaB). Muszę powiedzieć, że to narzędzie opracowane na własną rękę, więc mam nadzieję, że nie ma w tym pomyłki. Nie wahaj się mi powiedzieć, czy zauważyłeś jedną, może coś zrobiłem źle :) – sp00m

+0

Tak, podejrzewam, że 'boolean b = s instanceof Object [];' jest zoptymalizowane przez kompilator, ponieważ 's' jest' final 'i powyższe wyrażenie zawsze jest prawdziwe ... –

7

isArray() to najbardziej efektywny sposób sprawdzenia, czy obiekt jest instancją tablicy w środowisku wykonawczym. Jeśli wydajność jest problem, można użyć jednej z następujących metod, aby go rozwiązać:

  • byłaby kodu więc obiekty tablicy i obiekty spoza tablicy są obsługiwane oddzielnie, więc wyniki isArray() są znane w czasie kompilacji .
  • Użyj zmiennych lokalnych i/lub argumentów, aby buforować wartość isArray() podczas operacji, więc należy ją wywołać tylko raz.
+2

Zgadzam się z refaktoryzacji, ale czy ktoś może z powrotem odpowiedzieć na ich twierdzenia, że ​​'isArray()' jest tak dużo wolniejsza niż 'instanceof'? Uważam, że buforowanie wyniku może przynieść więcej szkody niż rozwiązać problemy tutaj! –

+0

Wygląda na to, że twoje (bezpodstawne) twierdzenie, że 'usArray()' jest najszybsze jest błędne - patrz odpowiedź sp00n. Zgadzam się jednak z sugestiami. – Bohemian

+0

@Bohemian: test sp00m może być niedokładny, zobacz komentarze ... –

2

Wychodząc z wniosku, stwierdzam, że podczas badania wyników profilowania może występować błąd interpretacyjny. Oprzyrządowanie na poziomie metody dla profilera może być znacznie okaleczające pod kątem wywołań getClass() i isArray(), a jednocześnie nie są wyrażane za pomocą wyrażeń instanceof. Innymi słowy, prawdopodobnie mierzysz obciążenie pomiarowe swojego profilera, tutaj.

Poza tym, w szybkim benchmarku, nie mogę poprzeć twojego roszczenia. Zabrakło mi następujące, bardzo głupie Test:

public class Test { 
    public static void main(String[] args) { 
     final int rep = 10000000; 
     Object[] o = { 
      null, 
      1, 
      "x", 
      new Object[0], 
      new Object[0][], 
      new int[0], 
      new int[0][] 
     }; 

     // "Warmup" to avoid potential JVM startup overhead 
     long x = 0; 
     for (int i = 0; i < rep; i++) { 
      x+=checkInstanceOf(o); 
     } 

     for (int i = 0; i < rep; i++) { 
      x+=checkIsArray(o); 
     } 

     for (int i = 0; i < rep; i++) { 
      x+=checkClassName(o); 
     } 

     // Actual test 
     long t1 = System.nanoTime(); 
     for (int i = 0; i < rep; i++) { 
      x+=checkInstanceOf(o); 
     } 

     long t2 = System.nanoTime(); 
     for (int i = 0; i < rep; i++) { 
      x+=checkIsArray(o); 
     } 

     long t3 = System.nanoTime(); 
     for (int i = 0; i < rep; i++) { 
      x+=checkClassName(o); 
     } 

     long t4 = System.nanoTime(); 

     System.out.println(t2 - t1); 
     System.out.println(t3 - t2); 
     System.out.println(t4 - t3); 
    } 

    private static int checkInstanceOf(Object[] o) { 
     int i = 0; 
     for (Object x : o) { 
      if (x instanceof Object[]) i++;  // Perform some logic 
      else if (x instanceof boolean[]) i++; // to keep the compiler or 
      else if (x instanceof byte[]) i++; // the JVM from optimising 
      else if (x instanceof short[]) i++; // this code away 
      else if (x instanceof int[]) i++; 
      else if (x instanceof long[]) i++; 
      else if (x instanceof float[]) i++; 
      else if (x instanceof double[]) i++; 
      else if (x instanceof char[]) i++; 
     } 
     return i; 
    } 

    private static int checkIsArray(Object[] o) { 
     int i = 0; 
     for (Object x : o) { 
      if (x != null && x.getClass().isArray()) i++; 
     } 
     return i; 
    } 

    private static int checkClassName(Object[] o) { 
     int i = 0; 
     for (Object x : o) { 
      if (x != null && x.getClass().getName().charAt(0) == '[') i++; 
     } 
     return i; 
    } 
} 

Dostaję:

394433000 // instanceof 
110655000 // getClass().isArray() 
396039000 // getClass().getName().charAt(0) == '[' 

Więc nie ogólnie roszczenie getClass().isArray() może być wolniejszy niż dokładnego zestawu instanceof kontroli. Oczywiście istnieje wiele różnych sposobów na przepisanie mojego testu, ale masz pomysł.

+1

Ten test porównawczy jest wadliwy. Nie uwzględnia rozgrzewki JVM. (Nie zdziwiłbym się, że uzyskałbyś przeciwne "wyniki", wykonując testy w innej kolejności ...) –

+0

@ StephenC: Oczywiście, że myślałem o tym i próbowałem obu sposobów. Zaktualizuję wyniki ... –

Powiązane problemy