2010-03-22 14 views
9

Lavorando in Java, supponiamo di avere due oggetti che, grazie a obj.getClass().isArray(), so che sono entrambi gli array. Diciamo inoltre che voglio confrontare questi due array l'uno con l'altro - possibilmente usando Arrays.equals. C'è un modo aggraziato per farlo senza ricorrere a un grande albero esaustivo se/else per capire quale sapore di Arrays.equals deve essere usato? Sto cercando qualcosa che sia meno peggio di questo:Confronto array Java


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

risposta

8

È possibile utilizzare la riflessione.

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

Riflesso viene utilizzato solo per trovare il metodo giusto in fase di esecuzione senza il pugno nell'occhio che si desidera evitare; il metodo effettivo Arrays.equals dovrebbe essere eseguito piuttosto velocemente.

Ovviamente la versione di produzione richiede una gestione delle eccezioni più efficace. È inoltre possibile utilizzare deepEquals(Object[], Object[]) anziché equals(Object[], Object[]) per gli array non primitivi.

+0

Esattamente quello che stavo cercando. Hai il mio ringraziamento. – BlairHippo

-1

Hai provato questo?

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

I metodi di overload non sono polimorfici di runtime. –

+0

Devi lanciarli in qualche modo, poiché non c'è sovraccarico di 'Arrays.equals (Object, Object)'. –

2

Ho paura che l'unica alternativa sarebbe utilizzare il riflesso, che sarebbe quasi brutto.

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

Non testato, potrebbe fallire in tutti i modi, ha bisogno di un sacco di gestione delle eccezioni ...

+0

Non è possibile utilizzare 'obj1.getClass()' direttamente poiché gli array non primitivi devono utilizzare l'overload 'Object []'. – polygenelubricants

+0

Dannazione! E 'stato un bel one-liner ... –

+0

Questo liner (che potrebbe essere reso ancora più bello con varargs) potrebbe funzionare anche una volta che gli array sono confermati primitivi. Il caso non primitivo potrebbe essere gestito separatamente, possibilmente usando invece "deepEquals". – polygenelubricants

0

È possibile utilizzare il metodo getClass() senza isArray(); controllare questo esempio:

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

ammetto fin da subito che questo è lontano da una soluzione perfetta. Accorcia la potenziale catena if-else che avevi nel post originale, ma causa errori se i tipi non sono uguali. Il seguente codice simile non sarebbe nemmeno compilare in 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)); 

se si è sicuri che non avrete di tipo discordanze e il vostro unico problema è che non si vuole capire che tipo hai, questo potrebbe funzionare.

+0

Come altri commentatori hanno notato, non esiste nulla come Arrays.equals (Object, Object); Avrei bisogno di lanciare obj1 e obj2 in qualcosa quando faccio la chiamata. – BlairHippo

+0

Avete un esempio di una situazione in cui ciò non funzionerebbe? Esiste un metodo "equals (Object [] a, Object [] a2)" e stai già verificando di avere array. – Pops

+0

I miei input sono una coppia di oggetti. Potrebbero essere di tipo Object [], o di tipo booleano [] o altro. Ma poiché Object []! = Object, non posso semplicemente passarli in Arrays.equals() raw e aspettarci che li gestisca; non ha il metodo necessario. E la conversione degli array in matrici Object [] esploderà se la provo su matrici di tipi primitivi. – BlairHippo

0

è possibile utilizzare il modello di strategia enum per creare un comparatore per ciascun 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); 
    } 

}

gestione degli errori omesso, ma, naturalmente, si vuole buttare IllegalArgumentException ovunque tipi non corretti sono passati in giro (Preferisco lasciare che ClassCastException sia generato da JVM e lanciare IAE nel mio codice quando rilevo qualcosa di sbagliato).

+0

Bene, pulisce parecchio il metodo equals(), che tecnicamente è quello che sto chiedendo. Ma sposta la complessità altrove, quindi penso che andrò con una soluzione diversa. – BlairHippo

+0

concordato. Le soluzioni più pulite finora sembrano essere basate sulla riflessione. Stavo solo fornendo un'alternativa che non usa la riflessione. – les2