j.u.Objects follow-up: deepEquals(Object, Object)?

Schulz, Stefan schulz at e-spirit.de
Fri Oct 9 09:18:20 UTC 2009

Joe wrote:
> Another piece of functionality requested in the j.u.Objects 
> thread was a 
> deepEquals(Object a, Object b.) method that "did the right 
> thing" if the 
> arguments happened to dynamically be arrays.
> I've been thinking a bit how this might be implemented.
> The array-ness of a and b would need to be determined, after any 
> up-front null-checks
> boolean aIsArray = a.getClass().isArray();
> boolean bIsArray = b.getClass().isArray();
> followed various case-analyses.
> if (aIsArray && bIsArray) {
>      Class<?> aComponentType = a.getClass().getComponentType();
>      Class<?> bComponentType = b.getClass().getComponentType();
>     if (aComponentType == bComponentType) {
>         // long case analysis to cast and call Arrays.deepEquals if 
> ComponentType is a reference type
>         // or the matching Arrays.equals(primitiveComponent[], 
> primitiveComponent[]) method if
>         // aComponentType.isPrimitive().
>     } else
>         return false;
> } else
>     return a.equals(b);
> Certainly a bit messy internally.

I've attached the implementation we use below. And, yes, it is a bit messy ;)
Two points, which might not be standard: if one of the argument is null or only one of the arguments is an array, the implementation returns false. I saw that your proposed equals() method does allow for an object to decide upon true or false, if only the second argument is null. As will the above approach allow for any object to decide upen true or false, if the given argument is an array. Both may or may not be desired, but usually should not according to the contract of equals().

> What are scenarios where this method would be used?

We use it in generic environments. For example, some event handling implementation, that fires only on data change, where the concrete type of data is not known (as this knowledge is not important for handling mechanisms). Another one are generic key-based data caches.


public static boolean deepEquals(final Object a, final Object b) {
  if (a == b) {
    return true;
  if (a == null || b == null) {
    return false;
  if (a.getClass().isArray()) {
    if ( ! b.getClass().isArray()) {
      return false;
    final Class<?> type = a.getClass().getComponentType();
    if (type.isPrimitive()) {
      if (b.getClass().getComponentType() != type) {
        return false;
      if (type == Byte.TYPE) {
        return Arrays.equals((byte[]) a, (byte[]) b);
      if (type == Short.TYPE) {
        return Arrays.equals((short[]) a, (short[]) b);
      if (type == Integer.TYPE) {
        return Arrays.equals((int[]) a, (int[]) b);
      if (type == Long.TYPE) {
        return Arrays.equals((long[]) a, (long[]) b);
      if (type == Character.TYPE) {
        return Arrays.equals((char[]) a, (char[]) b);
      if (type == Float.TYPE) {
        return Arrays.equals((float[]) a, (float[]) b);
      if (type == Double.TYPE) {
        return Arrays.equals((double[]) a, (double[]) b);
      if (type == Boolean.TYPE) {
        return Arrays.equals((boolean[]) a, (boolean[]) b);
    return Arrays.deepEquals((Object[]) a, (Object[]) b);
  return a.equals(b);

More information about the core-libs-dev mailing list