class TypeUtils {

    public static boolean equals(@Nullable Object left, @Nullable Object right) {
        if (left == right) { return true; }
        if (left == null || right == null) { return false; }

        // handle arrays on both sides as special case for efficiency
        Class<?> leftClass = left.getClass();
        Class<?> rightClass = right.getClass();
        if (leftClass.isArray() && rightClass.isArray()) {
            return arrayEqual(left, right);
        }
        if (leftClass.isArray() && leftClass.getComponentType().isPrimitive()) {
            left = primitiveArrayToList(left);
        }
        if (rightClass.isArray() && rightClass.getComponentType().isPrimitive()) {
            right = primitiveArrayToList(right);
        }
        if (left instanceof Object[] && right instanceof List) {
            return equals((Object[]) left, (List) right);
        }
        if (left instanceof List && right instanceof Object[]) {
            return equals((List) left, (Object[]) right);
        }
        if (left instanceof List && right instanceof List) {
            return equals((List) left, (List) right);
        }
        if (left instanceof Map.Entry && right instanceof Map.Entry) {
            Object k1 = ((Map.Entry) left).getKey();
            Object k2 = ((Map.Entry) right).getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = ((Map.Entry) left).getValue();
                Object v2 = ((Map.Entry) right).getValue();
                return v1 == v2 || (v1 != null && equals(v1, v2));
            }
            return false;
        }

        return left.equals(right);
    }

    private  TypeUtils();

}