Compute YangInstanceIdentifier.hashCode() lazily 47/98147/10
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 28 Oct 2021 11:41:05 +0000 (13:41 +0200)
committerRobert Varga <nite@hq.sk>
Sat, 23 Apr 2022 13:30:32 +0000 (13:30 +0000)
There are very few reasons to eagerly compute the hash code, let's not
do that.

JIRA: YANGTOOLS-1425
Change-Id: I0e76fa1cac539e318ee7a9bdcd28e6a6a3cbbb68
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/FixedYangInstanceIdentifier.java
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/PathArgumentList.java
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/StackedYangInstanceIdentifier.java
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifierBuilder.java

index 3d4fd97755044e5efaa680dcf98d17ab11b54b07..4a10de47a31492c71443a31b158add3f5b830212 100644 (file)
@@ -19,19 +19,22 @@ import org.opendaylight.yangtools.util.HashCodeBuilder;
 
 final class FixedYangInstanceIdentifier extends YangInstanceIdentifier implements Cloneable {
     static final @NonNull FixedYangInstanceIdentifier EMPTY_INSTANCE = new FixedYangInstanceIdentifier(
-        ImmutableList.of(), new HashCodeBuilder<>().build());
+        ImmutableList.of());
     private static final long serialVersionUID = 1L;
 
     private final ImmutableList<PathArgument> path;
     private transient volatile YangInstanceIdentifier parent;
 
-    FixedYangInstanceIdentifier(final ImmutableList<PathArgument> path, final int hash) {
-        super(hash);
+    FixedYangInstanceIdentifier(final ImmutableList<PathArgument> path) {
         this.path = requireNonNull(path, "path must not be null.");
     }
 
-    static @NonNull FixedYangInstanceIdentifier create(final Iterable<? extends PathArgument> path, final int hash) {
-        return new FixedYangInstanceIdentifier(ImmutableList.copyOf(path), hash);
+    static @NonNull FixedYangInstanceIdentifier of(final ImmutableList<PathArgument> path) {
+        return path.isEmpty() ? EMPTY_INSTANCE : new FixedYangInstanceIdentifier(path);
+    }
+
+    static @NonNull FixedYangInstanceIdentifier of(final List<PathArgument> path) {
+        return path.isEmpty() ? EMPTY_INSTANCE : new FixedYangInstanceIdentifier(ImmutableList.copyOf(path));
     }
 
     @Override
@@ -110,17 +113,8 @@ final class FixedYangInstanceIdentifier extends YangInstanceIdentifier implement
 
     @Override
     YangInstanceIdentifier createRelativeIdentifier(final int skipFromRoot) {
-        if (skipFromRoot == path.size()) {
-            return EMPTY_INSTANCE;
-        }
-
-        final ImmutableList<PathArgument> newPath = path.subList(skipFromRoot, path.size());
-        final HashCodeBuilder<PathArgument> hash = new HashCodeBuilder<>();
-        for (PathArgument a : newPath) {
-            hash.addArgument(a);
-        }
-
-        return new FixedYangInstanceIdentifier(newPath, hash.build());
+        return skipFromRoot == path.size() ? EMPTY_INSTANCE
+            : new FixedYangInstanceIdentifier(path.subList(skipFromRoot, path.size()));
     }
 
     private Object readResolve() throws ObjectStreamException {
@@ -128,11 +122,12 @@ final class FixedYangInstanceIdentifier extends YangInstanceIdentifier implement
     }
 
     @Override
-    boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
-        if (other instanceof FixedYangInstanceIdentifier) {
-            return path.equals(((FixedYangInstanceIdentifier) other).path);
+    int computeHashCode() {
+        int ret = 1;
+        for (PathArgument arg : path) {
+            ret = HashCodeBuilder.nextHashCode(ret, arg);
         }
-        return super.pathArgumentsEqual(other);
+        return ret;
     }
 
     @Override
index e41d79b711f193405978b8e50fdd928620028512..e0ff756808a4509558f5e075295fed9402717a62 100644 (file)
@@ -13,6 +13,7 @@ import java.util.Collection;
 import org.eclipse.jdt.annotation.NonNull;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 
+// FIXME: sealed once we have JDK17+
 abstract class PathArgumentList extends AbstractList<PathArgument> {
     @Override
     public abstract @NonNull UnmodifiableIterator<PathArgument> iterator();
index c8ff903394400ed140e2e1d8e777ace5e4954c83..b2cc4497da9eb7d41d6be587c1d022706f8a0964 100644 (file)
@@ -23,6 +23,7 @@ import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.List;
 import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.util.HashCodeBuilder;
 
 final class StackedYangInstanceIdentifier extends YangInstanceIdentifier implements Cloneable {
     private static final long serialVersionUID = 1L;
@@ -50,9 +51,7 @@ final class StackedYangInstanceIdentifier extends YangInstanceIdentifier impleme
     private transient volatile StackedPathArguments pathArguments;
     private transient volatile StackedReversePathArguments reversePathArguments;
 
-    StackedYangInstanceIdentifier(final YangInstanceIdentifier parent, final PathArgument pathArgument,
-            final int hash) {
-        super(hash);
+    StackedYangInstanceIdentifier(final YangInstanceIdentifier parent, final PathArgument pathArgument) {
         this.parent = requireNonNull(parent);
         this.pathArgument = requireNonNull(pathArgument);
     }
@@ -126,8 +125,7 @@ final class StackedYangInstanceIdentifier extends YangInstanceIdentifier impleme
                 current = stacked.getParent();
             } while (current.tryPathArguments() == null);
 
-            ret = new StackedPathArguments(current, Lists.reverse(stack));
-            pathArguments = ret;
+            pathArguments = ret = new StackedPathArguments(current, Lists.reverse(stack));
         }
 
         return ret;
@@ -164,6 +162,11 @@ final class StackedYangInstanceIdentifier extends YangInstanceIdentifier impleme
         return YangInstanceIdentifier.create(Iterables.skip(getPathArguments(), skipFromRoot));
     }
 
+    @Override
+    int computeHashCode() {
+        return HashCodeBuilder.nextHashCode(parent.hashCode(), pathArgument);
+    }
+
     @Override
     boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
         if (other instanceof StackedYangInstanceIdentifier) {
@@ -191,7 +194,7 @@ final class StackedYangInstanceIdentifier extends YangInstanceIdentifier impleme
         if (parent instanceof FixedYangInstanceIdentifier) {
             p = (FixedYangInstanceIdentifier) parent;
         } else {
-            p = FixedYangInstanceIdentifier.create(parent.getPathArguments(), parent.hashCode());
+            p = FixedYangInstanceIdentifier.of(parent.getPathArguments());
         }
         outputStream.writeObject(p);
     }
index 8c2204dd6d1a3890553833d834f2c2f531ef9303..f0d9627fe8dfbceadbbc41a521ab39525af0c192 100644 (file)
@@ -43,7 +43,6 @@ import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.concepts.HierarchicalIdentifier;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.concepts.Mutable;
-import org.opendaylight.yangtools.util.HashCodeBuilder;
 import org.opendaylight.yangtools.util.ImmutableOffsetMap;
 import org.opendaylight.yangtools.util.SingletonSet;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -76,30 +75,29 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
  *
  * @see <a href="http://tools.ietf.org/html/rfc6020#section-9.13">RFC6020</a>
  */
-// FIXME: 7.0.0: this concept needs to be moved to yang-common, as parser components need the ability to refer
-//               to data nodes -- most notably XPath expressions and {@code default} statement arguments need to be able
-//               to represent these.
+// FIXME: sealed once we have JDK17+
 public abstract class YangInstanceIdentifier implements HierarchicalIdentifier<YangInstanceIdentifier> {
     private static final long serialVersionUID = 4L;
     private static final VarHandle TO_STRING_CACHE;
+    private static final VarHandle HASH;
 
     static {
+        final var lookup = MethodHandles.lookup();
         try {
-            TO_STRING_CACHE = MethodHandles.lookup().findVarHandle(YangInstanceIdentifier.class, "toStringCache",
-                String.class);
+            HASH = lookup.findVarHandle(YangInstanceIdentifier.class, "hash", int.class);
+            TO_STRING_CACHE = lookup.findVarHandle(YangInstanceIdentifier.class, "toStringCache", String.class);
         } catch (NoSuchFieldException | IllegalAccessException e) {
             throw new ExceptionInInitializerError(e);
         }
     }
 
-
-    private final int hash;
+    @SuppressWarnings("unused")
+    private int hash;
     @SuppressWarnings("unused")
     private transient String toStringCache = null;
 
-    // Package-private to prevent outside subclassing
-    YangInstanceIdentifier(final int hash) {
-        this.hash = hash;
+    YangInstanceIdentifier() {
+        // Package-private to prevent outside subclassing
     }
 
     /**
@@ -114,9 +112,9 @@ public abstract class YangInstanceIdentifier implements HierarchicalIdentifier<Y
 
     abstract @NonNull YangInstanceIdentifier createRelativeIdentifier(int skipFromRoot);
 
-    abstract @Nullable Collection<PathArgument> tryPathArguments();
+    abstract @Nullable List<PathArgument> tryPathArguments();
 
-    abstract @Nullable Collection<PathArgument> tryReversePathArguments();
+    abstract @Nullable List<PathArgument> tryReversePathArguments();
 
     /**
      * Check if this instance identifier has empty path arguments, e.g. it is
@@ -184,21 +182,11 @@ public abstract class YangInstanceIdentifier implements HierarchicalIdentifier<Y
     public abstract PathArgument getLastPathArgument();
 
     public static @NonNull YangInstanceIdentifier create(final Iterable<? extends PathArgument> path) {
-        if (Iterables.isEmpty(path)) {
-            return empty();
-        }
-
-        final HashCodeBuilder<PathArgument> hash = new HashCodeBuilder<>();
-        for (PathArgument a : path) {
-            hash.addArgument(a);
-        }
-
-        return FixedYangInstanceIdentifier.create(path, hash.build());
+        return Iterables.isEmpty(path) ? empty() : new FixedYangInstanceIdentifier(ImmutableList.copyOf(path));
     }
 
     public static @NonNull YangInstanceIdentifier create(final PathArgument pathArgument) {
-        return new FixedYangInstanceIdentifier(ImmutableList.of(pathArgument),
-            HashCodeBuilder.nextHashCode(1, pathArgument));
+        return new FixedYangInstanceIdentifier(ImmutableList.of(pathArgument));
     }
 
     public static @NonNull YangInstanceIdentifier create(final PathArgument... path) {
@@ -240,23 +228,12 @@ public abstract class YangInstanceIdentifier implements HierarchicalIdentifier<Y
     }
 
     boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
-        return Iterables.elementsEqual(getPathArguments(), other.getPathArguments());
+        return getPathArguments().equals(other.getPathArguments());
     }
 
     @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof YangInstanceIdentifier)) {
-            return false;
-        }
-        YangInstanceIdentifier other = (YangInstanceIdentifier) obj;
-        if (this.hashCode() != obj.hashCode()) {
-            return false;
-        }
-
-        return pathArgumentsEqual(other);
+    public final boolean equals(final Object obj) {
+        return this == obj || obj instanceof YangInstanceIdentifier && pathArgumentsEqual((YangInstanceIdentifier) obj);
     }
 
     /**
@@ -276,7 +253,7 @@ public abstract class YangInstanceIdentifier implements HierarchicalIdentifier<Y
      * @return Instance Identifier with additional path argument added to the end.
      */
     public final @NonNull YangInstanceIdentifier node(final PathArgument arg) {
-        return new StackedYangInstanceIdentifier(this, arg, HashCodeBuilder.nextHashCode(hash, arg));
+        return new StackedYangInstanceIdentifier(this, arg);
     }
 
     /**
@@ -382,7 +359,8 @@ public abstract class YangInstanceIdentifier implements HierarchicalIdentifier<Y
          * Used lists, maps are immutable. Path Arguments (elements) are also
          * immutable, since the PathArgument contract requires immutability.
          */
-        return hash;
+        final int local = (int) HASH.getAcquire(this);
+        return local != 0 ? local : loadHashCode();
     }
 
     private static int hashCode(final Object value) {
@@ -407,6 +385,14 @@ public abstract class YangInstanceIdentifier implements HierarchicalIdentifier<Y
         return Objects.hashCode(value);
     }
 
+    private int loadHashCode() {
+        final int computed = computeHashCode();
+        HASH.setRelease(this, computed);
+        return computed;
+    }
+
+    abstract int computeHashCode();
+
     final Object writeReplace() {
         return new YIDv1(this);
     }
@@ -440,7 +426,7 @@ public abstract class YangInstanceIdentifier implements HierarchicalIdentifier<Y
      * @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
      */
     public static @NonNull InstanceIdentifierBuilder builder(final YangInstanceIdentifier origin) {
-        return new YangInstanceIdentifierBuilder(origin.getPathArguments(), origin.hashCode());
+        return new YangInstanceIdentifierBuilder(origin.getPathArguments());
     }
 
     /**
index 57a0a61e0f9b40e2d303f832b072189f6dd95c39..ff41e284c5e6809a5adaff1be3e630515fbf2dff 100644 (file)
@@ -14,7 +14,6 @@ import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.yangtools.util.HashCodeBuilder;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.InstanceIdentifierBuilder;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
@@ -22,22 +21,18 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdent
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 
 final class YangInstanceIdentifierBuilder implements InstanceIdentifierBuilder {
-    private final HashCodeBuilder<PathArgument> hash;
     private final List<PathArgument> path;
 
     YangInstanceIdentifierBuilder() {
-        this.hash = new HashCodeBuilder<>();
-        this.path = new ArrayList<>();
+        path = new ArrayList<>();
     }
 
-    YangInstanceIdentifierBuilder(final List<PathArgument> prefix, final int hash) {
-        this.path = new ArrayList<>(prefix);
-        this.hash = new HashCodeBuilder<>(hash);
+    YangInstanceIdentifierBuilder(final List<PathArgument> prefix) {
+        path = new ArrayList<>(prefix);
     }
 
     private @NonNull InstanceIdentifierBuilder addArgument(final PathArgument arg) {
         path.add(arg);
-        hash.addArgument(arg);
         return this;
     }
 
@@ -54,7 +49,6 @@ final class YangInstanceIdentifierBuilder implements InstanceIdentifierBuilder {
     @Override
     public InstanceIdentifierBuilder append(final Collection<? extends PathArgument> args) {
         path.addAll(args);
-        args.forEach(hash::addArgument);
         return this;
     }
 
@@ -70,6 +64,6 @@ final class YangInstanceIdentifierBuilder implements InstanceIdentifierBuilder {
 
     @Override
     public YangInstanceIdentifier build() {
-        return FixedYangInstanceIdentifier.create(path, hash.build());
+        return FixedYangInstanceIdentifier.of(path);
     }
 }