Java数组比较

9

在Java中工作,假设我有两个对象,通过obj.getClass().isArray(),我知道它们都是数组。进一步说,我想将这两个数组相互比较 - 可能使用Arrays.equals。是否有一种优雅的方法来做到这一点,而不必诉诸于大量详尽的if / else树来确定需要使用哪种Arrays.equals的版本?我正在寻找一些比这更少的令人疲倦的东西:


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

8
你可以使用反射。
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);
}

反射仅用于在运行时找到正确的方法,而不会产生你想避免的繁琐代码;实际的Arrays.equals方法应该运行得相当快。

显然,生产版本需要更健壮的异常处理。您可能还希望对非基本数组使用deepEquals(Object[], Object[])而不是equals(Object[], Object[])


2
我恐怕唯一的选择就是使用反射,这种方法几乎和之前的代码一样丑陋。
Arrays.getClass()
      .getMethod("equals", new Class[]{obj1.getClass(), obj2.getClass()})
      .invoke(null, new object[]{obj1, obj2});

未经测试,可能会出现各种问题,需要大量的异常处理...


由于非基本数组需要使用Object[]重载,因此您不能直接使用obj1.getClass() - polygenelubricants
这个一行代码(如果使用可变参数会更好)在确认数组是原始类型后仍然可以工作。非原始类型的情况可以单独处理,可能使用deepEquals代替。 - polygenelubricants
呵呵,不错。对于一个可行的解决方案加1,但这比我想要的另一种方向更进一步了;我尽量避免在别人需要维护的代码中进行高尔夫球比赛。 :-) - BlairHippo

0

您可以使用getClass()方法而不需要isArray()方法;请参考以下示例:

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

我先承认这远非完美解决方案。它缩短了原始帖子中可能庞大的if-else链,但如果类型不同则会导致错误。以下类似的代码甚至无法在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));

如果你有信心不会出现类型不匹配的问题,而且你唯一的问题是不想弄清楚你具体是哪种类型,那么这个方法可能适用。

正如其他评论者所指出的那样,Arrays.equals(Object, Object)并不存在;当我进行调用时,我需要将obj1和obj2转换为某些类型。 - BlairHippo
你有没有一个这种情况不适用的例子?已经有一个“equals(Object[] a, Object[] a2)”方法,而且你已经检查过你是否有数组。 - Pops
我的输入是一对对象。它们可以是Object[]类型,也可以是boolean[]类型或其他类型。但是因为Object[] != Object,所以我不能只是将它们原始地传递给Arrays.equals()并期望它处理它们;它没有必要的方法。如果我尝试将数组转换为Object[]数组,则会在尝试对基本类型的数组进行转换时失败。 - BlairHippo
啊,我不知道你正在传递对象(我可以建议你编辑您的帖子以反映这一点吗?)。在这种情况下,你当然是正确的。抱歉我不能更有帮助。 - Pops
没关系,我很感激你的努力。 :-) - BlairHippo

0

你可以使用枚举策略模式为每种类型创建比较器:

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

}

虽然省略了错误处理,但当传递了不正确的类型时,您肯定希望抛出IllegalArgumentException异常(我更喜欢让ClassCastException由JVM生成,在自己的代码中检测到问题时抛出IAE)。


好的,它大大简化了equals()方法,这从技术上讲是我所要求的。但它只是把复杂性转移到了其他地方,所以我想我会选择另一种解决方案。 - BlairHippo
同意。到目前为止,最清晰的解决方案似乎是基于反射的。我只是提供了一种不使用反射的替代方案。 - les2

0

-1
你试过这个吗?
// 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
重载的方法不是运行时多态的。 - Michael Borgwardt
你必须以某种方式进行强制转换,因为没有Arrays.equals(Object, Object)重载。 - Michael Myers

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接