9

Working within Java, let's say I have two objects that, thanks to obj.getClass().isArray(), I know are both arrays. Let's further say that I want to compare those two arrays to each other -- possibly by using Arrays.equals. Is there a graceful way to do this without resorting to a big exhaustive if/else tree to figure out which flavor of Arrays.equals needs to be used? I'm looking for something that's less of an eyesore than this:


      if (obj1 instanceof byte[] && obj2 instanceof byte[]) {
         return Arrays.equals((byte[])obj1, (byte[])obj2);
      }
      else if (obj1 instanceof boolean[] && obj2 instanceof boolean[]) {
         ...
BlairHippo
  • 9,502
  • 10
  • 54
  • 78

6 Answers6

8

You can use reflection.

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 is only used to find the right method at run-time without the eyesore you're looking to avoid; the actual Arrays.equals method should run pretty fast.

Obviously production version needs more robust exception handling. You may also want to use deepEquals(Object[], Object[]) instead of equals(Object[], Object[]) for non-primitive arrays.

polygenelubricants
  • 376,812
  • 128
  • 561
  • 623
2

I'm afraid the only alternative would be to use reflection, which would be nearly as ugly.

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

Not tested, could fail in all kinds of ways, needs lots of exception handling...

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • You can't use `obj1.getClass()` directly since non-primitive arrays need to use the `Object[]` overload. – polygenelubricants Mar 22 '10 at 16:21
  • This one liner (which could be made even nicer with varargs) could still work once arrays are confirmed primitive. The non-primitive case could be handled separately, possibly using `deepEquals` instead. – polygenelubricants Mar 22 '10 at 16:31
  • Heh. Nice. +1 for a workable solution, but this goes a little further the OTHER direction than I'd prefer; I try not to golf in code somebody else will have to maintain. :-) – BlairHippo Mar 22 '10 at 18:18
0

Maybe the Strategy Pattern can help it look better :)

mickthompson
  • 5,442
  • 11
  • 47
  • 59
0

You can use the getClass() method without isArray(); check out this example:

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

I admit up front that this is far from a perfect solution. It shortens the potentially huge if-else chain that you had in the original post, but causes errors if the types are not the same. The following similar code wouldn't even compile 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));

IF you are confident that you won't have type mismatches and your only problem is that you don't want to figure out which type you have, this could work.

Pops
  • 30,199
  • 37
  • 136
  • 151
  • As other commenters have noted, there's no such thing as Arrays.equals(Object, Object); I'd need to cast obj1 and obj2 into something when I make the call. – BlairHippo Mar 22 '10 at 16:22
  • Do you have an example of a situation in which this would not work? There IS a "equals(Object[] a, Object[] a2)" method, and you're already checking that you have arrays. – Pops Mar 22 '10 at 16:31
  • My inputs are a pair of Objects. They could be of type Object[], or of type boolean[], or whatever. But because Object[] != Object, I can't just pass them in to Arrays.equals() raw and expect it to handle them; it doesn't have the necessary method. And converting the arrays to Object[] arrays will blow up if I try it on arrays of primitive types. – BlairHippo Mar 22 '10 at 16:39
  • Ah, I didn't know you were passing Objects in (may I suggest that you edit your post to reflect that?). In that case, you are of course correct. Sorry I couldn't be more helpful. – Pops Mar 22 '10 at 16:45
  • No worries, I appreciate the effort. :-) – BlairHippo Mar 22 '10 at 16:47
0

you can use the enum strategy pattern to create a comparator for each type:

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

}

Error handling omitted, but of course, you want to throw IllegalArgumentException wherever incorrect types are passed around (I prefer to let ClassCastException be JVM generated, and throw IAE in my own code when I detect something wrong).

les2
  • 14,093
  • 16
  • 59
  • 76
  • Well, it cleans-up the equals() method quite a lot, which is technically what I'm asking for. But it just moves the complexity elsewhere, so I think I'll go with a different solution. – BlairHippo Mar 22 '10 at 16:40
  • Agreed. The cleanest solutions so far seem to be the reflection-based. I was just providing a alternative that doesn't use reflection. – les2 Mar 22 '10 at 17:54
-1

Have you tried this ?

// Test if both arrays are of the same type
if (array1.class.getComponentType.equals(array2.class.getComponentTYpe)) {
    // Polymorphism FTW !
    return Arrays.equals(array1, array2);
}
Olivier Croisier
  • 6,139
  • 25
  • 34