BUG-592: Optimize equals() a bit 86/5786/4
authorRobert Varga <rovarga@cisco.com>
Wed, 26 Mar 2014 18:50:06 +0000 (19:50 +0100)
committerTony Tkacik <ttkacik@cisco.com>
Wed, 23 Apr 2014 08:04:49 +0000 (08:04 +0000)
This commit optimizes the equals() method by attempting to bypass the
full check of iterables by looking at cached state that is derived from
them.

Change-Id: I10b6e7164874992ae0dc041cffd934d292e61694
Signed-off-by: Robert Varga <rovarga@cisco.com>
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifier.java
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/KeyedInstanceIdentifier.java

index 04f614da9385bd40709b75eadf2f648f4913503b..7f46d15f7d91c8bcceb000cb87c05f37cc27cfd7 100644 (file)
@@ -103,7 +103,7 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
     }
 
     @Override
-    public boolean equals(final Object obj) {
+    public final boolean equals(final Object obj) {
         if (this == obj) {
             return true;
         }
@@ -115,13 +115,45 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
         }
 
         final InstanceIdentifier<?> other = (InstanceIdentifier<?>) obj;
+        if (pathArguments == other.pathArguments) {
+            return true;
+        }
+
+        /*
+         * We could now just go and compare the pathArguments, but that
+         * can be potentially expensive. Let's try to avoid that by
+         * checking various things that we have cached from pathArguments
+         * and trying to prove the identifiers are *not* equal.
+         */
         if (hash != other.hash) {
             return false;
         }
+        if (wildcarded != other.wildcarded) {
+            return false;
+        }
+        if (targetType != other.targetType) {
+            return false;
+        }
+        if (fastNonEqual(other)) {
+            return false;
+        }
 
+        // Everything checks out so far, so we have to do a full equals
         return Iterables.elementsEqual(pathArguments, other.pathArguments);
     }
 
+    /**
+     * Perform class-specific fast checks for non-equality. This allows
+     * subclasses to avoid iterating over the pathArguments by performing
+     * quick checks on their specific fields.
+     *
+     * @param other The other identifier, guaranteed to be the same class
+     * @return @true if the other identifier cannot be equal to this one.
+     */
+    protected boolean fastNonEqual(final InstanceIdentifier<?> other) {
+        return false;
+    }
+
     @Override
     public final String toString() {
         return addToStringAttributes(Objects.toStringHelper(this)).toString();
index 8d9dcdc93756ea8c809fcaa2e4afb4d3e54e031c..6b6e05c7188c0788434d081615e288b6da6b580f 100644 (file)
@@ -23,4 +23,16 @@ public class KeyedInstanceIdentifier<T extends Identifiable<K> & DataObject, K e
     public final InstanceIdentifierBuilder<T> builder() {
         return new InstanceIdentifierBuilderImpl<T>(new InstanceIdentifier.IdentifiableItem<T, K>(getTargetType(), key), getPathArguments(), hashCode(), isWildcarded());
     }
+
+    @Override
+    protected boolean fastNonEqual(final InstanceIdentifier<?> other) {
+        final KeyedInstanceIdentifier<?, ?> kii = (KeyedInstanceIdentifier<?, ?>) other;
+
+        /*
+         * We could do an equals() here, but that may actually be expensive.
+         * equals() in superclass falls back to a full compare, which will
+         * end up running that equals anyway, so do not bother here.
+         */
+        return (key == null) != (kii.key == null);
+    }
 }