2010-03-22 19 views
9

Praca w Javie, załóżmy, że mam dwa obiekty, które dzięki obj.getClass().isArray(), wiem, że obie są tablicami. Powiedzmy, że chcę porównać te dwie tablice ze sobą - prawdopodobnie używając Arrays.equals. Czy jest na to dobry sposób, bez uciekania się do dużego, wyczerpującego drzewa if/else, aby dowiedzieć się, który aromat Arrays.equals należy zastosować? Szukam czegoś, co jest mniej odrażające niż to:Porównanie Java Array


     if (obj1 instanceof byte[] && obj2 instanceof byte[]) { 
     return Arrays.equals((byte[])obj1, (byte[])obj2); 
     } 
     else if (obj1 instanceof boolean[] && obj2 instanceof boolean[]) { 
     ... 

Odpowiedz

8

Można użyć refleksji.

public static boolean arrayEquals(Object arr1, Object arr2) throws Exception { 
    Class<?> c = arr1.getClass(); 
    if (!c.getComponentType().isPrimitive()) { 
     c = Object[].class; 
    } 
    Method m = Arrays.class.getMethod("equals", c, c); 
    return (Boolean) m.invoke(null, arr1, arr2); 
} 

Odbicie służy tylko do znalezienia właściwej metody w czasie wykonywania bez efektu wzrokowego, którego chcesz unikać; Rzeczywista metoda Arrays.equals powinna działać dość szybko.

Oczywiście wersja produkcyjna wymaga bardziej niezawodnej obsługi wyjątków. Możesz także użyć deepEquals(Object[], Object[]) zamiast equals(Object[], Object[]) dla tablic niepochodzących z prymitywów.

+0

Dokładnie to, czego szukałem. Masz moje podziękowania. – BlairHippo

-1

Czy próbowałeś tego?

// Test if both arrays are of the same type 
if (array1.class.getComponentType.equals(array2.class.getComponentTYpe)) { 
    // Polymorphism FTW ! 
    return Arrays.equals(array1, array2); 
} 
+1

Przeciążone metody nie są polimorficzne. –

+0

Musisz je jakoś rzucić, ponieważ nie ma przeciążenia 'Arrays.equals (Object, Object)'. –

2

Obawiam się, że jedyną alternatywą byłoby użycie refleksji, która byłaby prawie tak brzydka.

Arrays.getClass() 
     .getMethod("equals", new Class[]{obj1.getClass(), obj2.getClass()}) 
     .invoke(null, new object[]{obj1, obj2}); 

nie testowano, może nie we wszystkich rodzajach dróg, potrzebuje dużo obsługi wyjątków ...

+0

Nie można użyć 'obj1.getClass()' bezpośrednio, ponieważ nieproste tablice muszą używać przeciążenia 'Object []'. – polygenelubricants

+0

Cholera! To było takie miłe jedno-liniowe ... –

+0

Ta jedna liniówka (która mogłaby być jeszcze ładniejsza dzięki varargs) mogłaby nadal działać, gdy tablice zostaną potwierdzone jako prymitywne. Niezwykły przypadek może być obsługiwany osobno, prawdopodobnie używając zamiast tego 'deepEquals'. – polygenelubricants

0

Możesz użyć metody getClass() bez isArray(); sprawdź ten przykład:

byte[] foo = { 1, 2 }; 
byte[] bar = { 1, 2 }; 

System.out.println(foo.getClass()); 
System.out.println(bar.getClass()); 

if(foo.getClass() == bar.getClass()) 
    System.out.println(Arrays.equals(foo, bar)); 

Przyznam góry, że jest to dalekie od idealnego rozwiązania. Skraca potencjalnie ogromny łańcuch if-else, który był w oryginalnym wpisie, ale powoduje błędy, jeśli typy nie są takie same. Poniższy kod podobny nawet nie skompilować w MyEclipse 8.0:

byte[] obj1 = { 1, 2 }; 
String[] obj2 = { "1", "2" }; 

System.out.println(obj1.getClass()); 
System.out.println(obj2.getClass()); 

if(obj1.getClass().toString().equals(obj2.getClass().toString())) 
    System.out.println(Arrays.equals(obj1, obj2)); 

jeśli jesteś pewien, że nie będzie musiał niedopasowania typu i swoją Jedynym problemem jest to, że nie chcesz, aby dowiedzieć który typ masz, to może zadziałać.

+0

Jak zauważyli inni komentatorzy, nie ma czegoś takiego jak Arrays.equals (Object, Object); Kiedy wykonam połączenie, będę musiał użyć funkcji obj1 i obj2 w coś. – BlairHippo

+0

Czy masz przykład sytuacji, w której to nie zadziała? Istnieje metoda "równa się (Object [] a, Object [] a2)", a już sprawdzasz, czy masz tablice. – Pops

+0

Moje wejścia to para obiektów. Mogą być typu Object [] lub typu boolean [] lub cokolwiek innego. Ale ponieważ Object []! = Object, nie mogę po prostu przekazać ich do Arrays.equals() raw i oczekiwać, że sobie z nimi poradzi; nie ma niezbędnej metody. A konwersja tablic na tablice Object [] spowoduje wybuch, jeśli spróbuję tego na tablicach typów pierwotnych. – BlairHippo

0

można użyć wzoru strategii enum stworzyć porównawczy dla każdego typu:

public enum ArrayEq { 
    BYTE_PRIMITIVE(Byte.TYPE) { 
     protected boolean doEquals(Object array1, Object array2) { 
      return Arrays.equals((byte[]) array1, (byte[]) array2); 
     } 
    }, 
    ... enum element for each component type 

    private final Class<?> type; 

    private ArrayEq(final Class<?> type) { this.type = type; } 
    public Class<?> getComponentType() { return type; } 

    // force all enums to implement this method 
    protected abstract boolean doEquals(Object array1, Object array2); 

    public static boolean equals(Object array1, Object array2) { 
     if(array1 == null) return array2 == null; 

     // you need to populate this map in a static initializer of the enum 
     // a simple linear search would work too since the number of elements is small 
     typeToElementMap.get(array1.getComponentType()) 
      .doEquals(array1, array2); 
    } 

}

obsługi błędów pominięte, ale oczywiście, chcesz rzucić IllegalArgumentException gdziekolwiek niepoprawne typy są przekazywane wokół (Wolę, aby ClassCastException generował JVM, i wyrzuć IAE w moim własnym kodzie, gdy wykryję coś nie tak).

+0

Cóż, dość dużo czyści metodę equals(), czego technicznie wymagam. Ale to po prostu przesuwa złożoność gdzie indziej, więc myślę, że pójdę z innym rozwiązaniem. – BlairHippo

+0

Uzgodnione. Najczystsze rozwiązania do tej pory wydają się oparte na refleksji. Po prostu dostarczałem alternatywę, która nie wykorzystuje refleksji. – les2