2010-03-22 30 views
9

Trabajando dentro de Java, digamos que tengo dos objetos que, gracias a obj.getClass().isArray(), sé que son dos matrices. Digamos además que quiero comparar esas dos matrices entre sí, posiblemente usando Arrays.equals. ¿Hay una manera elegante de hacerlo sin tener que recurrir a un gran árbol exhaustivo para averiguar qué sabor de Arrays.equals necesita ser utilizado? Estoy buscando algo que no sea tan desagradable como este:Comparación de matrices Java


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

Respuesta

8

Puede utilizar la reflexión.

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

Reflection solo se utiliza para encontrar el método correcto en tiempo de ejecución sin la monstruosidad que desea evitar; el método real Arrays.equals debe ejecutarse bastante rápido.

Obviamente, la versión de producción necesita un manejo de excepciones más robusto. Es posible que también desee utilizar deepEquals(Object[], Object[]) en lugar de equals(Object[], Object[]) para matrices no primitivas.

+0

Exactamente lo que estaba buscando. Tienes mi agradecimiento – BlairHippo

-1

¿Has probado esto?

// 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

Los métodos sobrecargados no son polimórficos en tiempo de ejecución. –

+0

Tienes que lanzarlos de alguna manera, ya que no hay una sobrecarga 'Arrays.equals (Object, Object)'. –

2

Me temo que la única alternativa sería usar el reflejo, que sería casi tan feo.

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

No se ha probado, podría fallar en todo tipo de formas, necesita un montón de manejo de excepciones ...

+0

No puede usar 'obj1.getClass()' directamente ya que las matrices no primitivas necesitan usar la sobrecarga 'Object []'. – polygenelubricants

+0

¡Maldición! Fue un buen one-line ... –

+0

Este delineador (que podría hacerse aún más bonito con varargs) aún podría funcionar una vez que las matrices se confirman como primitivas. El caso no primitivo se podría manejar por separado, posiblemente usando 'deepEquals' en su lugar. – polygenelubricants

0

Puede utilizar el método getClass() sin isArray(); echa un vistazo a este ejemplo:

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

Admito desde el principio que esto está lejos de una solución perfecta. Acorta la cadena if-else potencialmente enorme que tenía en la publicación original, pero causa errores si los tipos no son los mismos. El siguiente código similar ni siquiera se compila en 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)); 

si está seguro de que usted no tendrá desajustes de tipo y el único problema es que usted no quiere averiguar lo que Tipo usted tiene, esto podría funcionar.

+0

Tal como lo han señalado otros comentaristas, no existe Arrays.equals (Object, Object); Tendría que convertir obj1 y obj2 en algo cuando realice la llamada. – BlairHippo

+0

¿Tiene un ejemplo de una situación en la que esto no funcionaría? HAY un método "igual (Objeto [] a, Objeto [] a2)", y ya está comprobando que tiene matrices. – Pops

+0

Mis entradas son un par de Objetos. Podrían ser de tipo Object [], o de tipo boolean [], o lo que sea. Pero debido a Object []! = Object, no puedo simplemente pasarlos a Arrays.equals() en bruto y esperar que los maneje; no tiene el método necesario. Y convertir las matrices en matrices Object [] explotará si lo pruebo en matrices de tipos primitivos. – BlairHippo

0

puede utilizar el patrón de estrategia de enumeración para crear un comparador para cada tipo:

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

} manipulación omite

error, pero por supuesto, usted quiere lanzar IllegalArgumentException donde se pasan los tipos incorrectos alrededor (Prefiero dejar que ClassCastException sea generado por JVM, y lanzar IAE en mi propio código cuando detecto algo incorrecto).

+0

Bueno, limpia mucho el método equals(), que técnicamente es lo que estoy pidiendo. Pero solo mueve la complejidad a otra parte, así que creo que iré con una solución diferente. – BlairHippo

+0

De acuerdo. Las soluciones más limpias hasta ahora parecen estar basadas en la reflexión. Solo estaba proporcionando una alternativa que no usa el reflejo. – les2