United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-6891113 : More methods for java.util.Objects: deepEquals, hash, toString with default

Details
Type:
Bug
Submit Date:
2009-10-13
Status:
Resolved
Updated Date:
2010-04-02
Project Name:
JDK
Resolved Date:
2009-11-24
Component:
core-libs
OS:
linux,generic
Sub-Component:
java.util
CPU:
x86,generic
Priority:
P3
Resolution:
Fixed
Affected Versions:
5.0,7
Fixed Versions:

Related Reports
Duplicate:
Relates:
Relates:
Relates:

Sub Tasks

Description
Further discussion on the corelibs mailing list about utility methods suitable for java.util.Objects revealed several worthwhile additions including:

static toString(Object o, String default): toString taking a default if o is null
static deepEquals(Object a, Object b): run deep equals on arbritrary arguments
static hash(Object... objs): compute a hash code for inputs

                                    

Comments
SUGGESTED FIX

# HG changeset patch
# User darcy
# Date 1256321897 25200
# Node ID 80102c165e09e45aa3814e3db5aac20ae2ecbdf6
# Parent 93f8d9494e054ee095119ad2dd16d06d76b6fde8
6891113: More methods for java.util.Objects: deepEquals, hash, toString with default
Reviewed-by: alanb, gafter

--- a/src/share/classes/java/util/Arrays.java	Thu Oct 22 15:44:42 2009 +0100
+++ b/src/share/classes/java/util/Arrays.java	Fri Oct 23 11:18:17 2009 -0700
@@ -3928,6 +3928,7 @@ public class Arrays {
      * @param a2 the other array to be tested for equality
      * @return <tt>true</tt> if the two arrays are equal
      * @see #equals(Object[],Object[])
+     * @see Objects#deepEquals(Object, Object)
      * @since 1.5
      */
     public static boolean deepEquals(Object[] a1, Object[] a2) {
@@ -3949,32 +3950,38 @@ public class Arrays {
                 return false;
 
             // Figure out whether the two elements are equal
-            boolean eq;
-            if (e1 instanceof Object[] && e2 instanceof Object[])
-                eq = deepEquals ((Object[]) e1, (Object[]) e2);
-            else if (e1 instanceof byte[] && e2 instanceof byte[])
-                eq = equals((byte[]) e1, (byte[]) e2);
-            else if (e1 instanceof short[] && e2 instanceof short[])
-                eq = equals((short[]) e1, (short[]) e2);
-            else if (e1 instanceof int[] && e2 instanceof int[])
-                eq = equals((int[]) e1, (int[]) e2);
-            else if (e1 instanceof long[] && e2 instanceof long[])
-                eq = equals((long[]) e1, (long[]) e2);
-            else if (e1 instanceof char[] && e2 instanceof char[])
-                eq = equals((char[]) e1, (char[]) e2);
-            else if (e1 instanceof float[] && e2 instanceof float[])
-                eq = equals((float[]) e1, (float[]) e2);
-            else if (e1 instanceof double[] && e2 instanceof double[])
-                eq = equals((double[]) e1, (double[]) e2);
-            else if (e1 instanceof boolean[] && e2 instanceof boolean[])
-                eq = equals((boolean[]) e1, (boolean[]) e2);
-            else
-                eq = e1.equals(e2);
+            boolean eq = deepEquals0(e1, e2);
 
             if (!eq)
                 return false;
         }
         return true;
+    }
+
+    static boolean deepEquals0(Object e1, Object e2) {
+        assert e1 != null;
+        boolean eq;
+        if (e1 instanceof Object[] && e2 instanceof Object[])
+            eq = deepEquals ((Object[]) e1, (Object[]) e2);
+        else if (e1 instanceof byte[] && e2 instanceof byte[])
+            eq = equals((byte[]) e1, (byte[]) e2);
+        else if (e1 instanceof short[] && e2 instanceof short[])
+            eq = equals((short[]) e1, (short[]) e2);
+        else if (e1 instanceof int[] && e2 instanceof int[])
+            eq = equals((int[]) e1, (int[]) e2);
+        else if (e1 instanceof long[] && e2 instanceof long[])
+            eq = equals((long[]) e1, (long[]) e2);
+        else if (e1 instanceof char[] && e2 instanceof char[])
+            eq = equals((char[]) e1, (char[]) e2);
+        else if (e1 instanceof float[] && e2 instanceof float[])
+            eq = equals((float[]) e1, (float[]) e2);
+        else if (e1 instanceof double[] && e2 instanceof double[])
+            eq = equals((double[]) e1, (double[]) e2);
+        else if (e1 instanceof boolean[] && e2 instanceof boolean[])
+            eq = equals((boolean[]) e1, (boolean[]) e2);
+        else
+            eq = e1.equals(e2);
+        return eq;
     }
 
     /**
--- a/src/share/classes/java/util/Objects.java	Thu Oct 22 15:44:42 2009 +0100
+++ b/src/share/classes/java/util/Objects.java	Fri Oct 23 11:18:17 2009 -0700
@@ -33,7 +33,7 @@ package java.util;
  *
  * @since 1.7
  */
-public class Objects {
+public final class Objects {
     private Objects() {
         throw new AssertionError("No java.util.Objects instances for you!");
     }
@@ -57,6 +57,32 @@ public class Objects {
         return (a == b) || (a != null && a.equals(b));
     }
 
+   /**
+    * Returns {@code true} if the arguments are deeply equal to each other
+    * and {@code false} otherwise.
+    *
+    * Two {@code null} values are deeply equal.  If both arguments are
+    * arrays, the algorithm in {@link Arrays#deepEquals(Object[],
+    * Object[]) Arrays.deepEquals} is used to determine equality.
+    * Otherwise, equality is determined by using the {@link
+    * Object#equals equals} method of the first argument.
+    *
+    * @param a an object
+    * @param b an object to be compared with {@code a} for deep equality
+    * @return {@code true} if the arguments are deeply equal to each other
+    * and {@code false} otherwise
+    * @see Arrays#deepEquals(Object[], Object[])
+    * @see Objects#equals(Object, Object)
+    */
+    public static boolean deepEquals(Object a, Object b) {
+        if (a == b)
+            return true;
+        else if (a == null || b == null)
+            return false;
+        else
+            return Arrays.deepEquals0(a, b);
+    }
+
     /**
      * Returns the hash code of a non-{@code null} argument and 0 for
      * a {@code null} argument.
@@ -68,6 +94,36 @@ public class Objects {
      */
     public static int hashCode(Object o) {
         return o != null ? o.hashCode() : 0;
+    }
+
+   /**
+    * Generates a hash code for a sequence of input values. The hash
+    * code is generated as if all the input values were placed into an
+    * array, and that array were hashed by calling {@link
+    * Arrays#hashCode(Object[])}.
+    *
+    * <p>This method is useful for implementing {@link
+    * Object#hashCode()} on objects containing multiple fields. For
+    * example, if an object that has three fields, {@code x}, {@code
+    * y}, and {@code z}, one could write:
+    *
+    * <blockquote><pre>
+    * &#064;Override public int hashCode() {
+    *     return Objects.hash(x, y, z);
+    * }
+    * </pre></blockquote>
+    *
+    * <b>Warning: When a single object reference is supplied, the returned
+    * value does not equal the hash code of that object reference.</b> This
+    * value can be computed by calling {@link #hashCode(Object)}.
+    *
+    * @param values the values to be hashed
+    * @return a hash value of the sequence of input values
+    * @see Arrays#hashCode
+    * @see List#hashCode
+    */
+    public static int hash(Object... values) {
+        return Arrays.hashCode(values);
     }
 
     /**
@@ -82,6 +138,23 @@ public class Objects {
      */
     public static String toString(Object o) {
         return String.valueOf(o);
+    }
+
+    /**
+     * Returns the result of calling {@code toString} on the first
+     * argument if the first argument is not {@code null} and returns
+     * the second argument otherwise.
+     *
+     * @param o an object
+     * @param nullDefault string to return if the first argument is
+     *        {@code null}
+     * @return the result of calling {@code toString} on the first
+     * argument if it is not {@code null} and the second argument
+     * otherwise.
+     * @see Objects#toString(Object)
+     */
+    public static String toString(Object o, String nullDefault) {
+        return (o != null) ? o.toString() : nullDefault;
     }
 
     /**
--- a/test/java/util/Objects/BasicObjectsTest.java	Thu Oct 22 15:44:42 2009 +0100
+++ b/test/java/util/Objects/BasicObjectsTest.java	Fri Oct 23 11:18:17 2009 -0700
@@ -23,7 +23,7 @@
 
 /*
  * @test
- * @bug 6797535
+ * @bug 6797535 6889858 6891113
  * @summary Basic tests for methods in java.util.Objects
  * @author  Joseph D. Darcy
  */
@@ -34,8 +34,11 @@ public class BasicObjectsTest {
     public static void main(String... args) {
         int errors = 0;
         errors += testEquals();
+        errors += testDeepEquals();
         errors += testHashCode();
+        errors += testHash();
         errors += testToString();
+        errors += testToString2();
         errors += testCompare();
         errors += testNonNull();
         if (errors > 0 )
@@ -60,6 +63,36 @@ public class BasicObjectsTest {
         return errors;
     }
 
+    private static int testDeepEquals() {
+        int errors = 0;
+        Object[] values = {null,
+                           null, // Change to values later
+                           new byte[]  {(byte)1},
+                           new short[] {(short)1},
+                           new int[]   {1},
+                           new long[]  {1L},
+                           new char[]  {(char)1},
+                           new float[] {1.0f},
+                           new double[]{1.0d},
+                           new String[]{"one"}};
+        values[1] = values;
+
+        for(int i = 0; i < values.length; i++)
+            for(int j = 0; j < values.length; j++) {
+                boolean expected = (i == j);
+                Object a = values[i];
+                Object b = values[j];
+                boolean result = Objects.deepEquals(a, b);
+                if (result != expected) {
+                    errors++;
+                    System.err.printf("When equating %s to %s, got %b instead of %b%n.",
+                                      a, b, result, expected);
+                }
+            }
+
+        return errors;
+    }
+
     private static int testHashCode() {
         int errors = 0;
         errors += (Objects.hashCode(null) == 0 ) ? 0 : 1;
@@ -68,11 +101,32 @@ public class BasicObjectsTest {
         return errors;
     }
 
+    private static int testHash() {
+        int errors = 0;
+
+        Object[] data = new String[]{"perfect", "ham", "THC"};
+
+        errors += ((Objects.hash((Object[])null) == 0) ? 0 : 1);
+
+        errors += (Objects.hash("perfect", "ham", "THC") ==
+                   Arrays.hashCode(data)) ? 0 : 1;
+
+        return errors;
+    }
+
     private static int testToString() {
         int errors = 0;
         errors += ("null".equals(Objects.toString(null)) ) ? 0 : 1;
         String s = "Some string";
         errors += (s.equals(Objects.toString(s)) ) ? 0 : 1;
+        return errors;
+    }
+
+    private static int testToString2() {
+        int errors = 0;
+        String s = "not the default";
+        errors += (s.equals(Objects.toString(null, s)) ) ? 0 : 1;
+        errors += (s.equals(Objects.toString(s, "another string")) ) ? 0 : 1;
         return errors;
     }
                                     
2009-10-23
PUBLIC COMMENTS

See
http://hg.openjdk.java.net/jdk7/tl/jdk/rev/80102c165e09
                                     
2009-10-23
EVALUATION

A fine idea.
                                     
2009-10-13



Hardware and Software, Engineered to Work Together