This is an automated email from the ASF dual-hosted git repository.

cstamas pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git


The following commit(s) were added to refs/heads/master by this push:
     new 93bd1cb0 DepMgr hashCode handling (#749)
93bd1cb0 is described below

commit 93bd1cb084e756beb3e544d368afaf006082f557
Author: Tamas Cservenak <[email protected]>
AuthorDate: Thu Jun 12 23:16:55 2025 +0200

    DepMgr hashCode handling (#749)
    
    As graph is growing, same instances of HashMaps are repeatedly asked for 
hashCode, that in default implementation goes over all keys and values over and 
over again.
    
    Changes:
    * fix missing entry in equals as @mbien reported
    * "invent" a special construct that fits this very use case: map is 
modified once, and copied if next level is about to modify it
    * the construct once asked for hashCode, memoizes it (the "read only" check 
never happens in this code, is just there to be foolproof)
---
 .../graph/manager/AbstractDependencyManager.java   | 104 +++++++++++--------
 .../graph/manager/ClassicDependencyManager.java    |  21 ++--
 .../graph/manager/DefaultDependencyManager.java    |  21 ++--
 .../eclipse/aether/util/graph/manager/MMap.java    | 114 +++++++++++++++++++++
 .../graph/manager/TransitiveDependencyManager.java |  21 ++--
 5 files changed, 208 insertions(+), 73 deletions(-)

diff --git 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/AbstractDependencyManager.java
 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/AbstractDependencyManager.java
index 0b9c1cf7..90bb3c1e 100644
--- 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/AbstractDependencyManager.java
+++ 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/AbstractDependencyManager.java
@@ -20,10 +20,8 @@ package org.eclipse.aether.util.graph.manager;
 
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
-import java.util.Map;
 import java.util.Objects;
 
 import org.eclipse.aether.artifact.Artifact;
@@ -64,15 +62,15 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
 
     protected final int applyFrom;
 
-    protected final Map<Object, Holder<String>> managedVersions;
+    protected final MMap<Key, Holder<String>> managedVersions;
 
-    protected final Map<Object, Holder<String>> managedScopes;
+    protected final MMap<Key, Holder<String>> managedScopes;
 
-    protected final Map<Object, Holder<Boolean>> managedOptionals;
+    protected final MMap<Key, Holder<Boolean>> managedOptionals;
 
-    protected final Map<Object, Holder<String>> managedLocalPaths;
+    protected final MMap<Key, Holder<String>> managedLocalPaths;
 
-    protected final Map<Object, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions;
+    protected final MMap<Key, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions;
 
     protected final SystemDependencyScope systemDependencyScope;
 
@@ -83,11 +81,11 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
                 0,
                 deriveUntil,
                 applyFrom,
-                Collections.emptyMap(),
-                Collections.emptyMap(),
-                Collections.emptyMap(),
-                Collections.emptyMap(),
-                Collections.emptyMap(),
+                MMap.empty(),
+                MMap.empty(),
+                MMap.empty(),
+                MMap.empty(),
+                MMap.empty(),
                 scopeManager != null
                         ? scopeManager.getSystemDependencyScope().orElse(null)
                         : SystemDependencyScope.LEGACY);
@@ -98,11 +96,11 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
             int depth,
             int deriveUntil,
             int applyFrom,
-            Map<Object, Holder<String>> managedVersions,
-            Map<Object, Holder<String>> managedScopes,
-            Map<Object, Holder<Boolean>> managedOptionals,
-            Map<Object, Holder<String>> managedLocalPaths,
-            Map<Object, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions,
+            MMap<Key, Holder<String>> managedVersions,
+            MMap<Key, Holder<String>> managedScopes,
+            MMap<Key, Holder<Boolean>> managedOptionals,
+            MMap<Key, Holder<String>> managedLocalPaths,
+            MMap<Key, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions,
             SystemDependencyScope systemDependencyScope) {
         this.depth = depth;
         this.deriveUntil = deriveUntil;
@@ -127,11 +125,11 @@ public abstract class AbstractDependencyManager 
implements DependencyManager {
     }
 
     protected abstract DependencyManager newInstance(
-            Map<Object, Holder<String>> managedVersions,
-            Map<Object, Holder<String>> managedScopes,
-            Map<Object, Holder<Boolean>> managedOptionals,
-            Map<Object, Holder<String>> managedLocalPaths,
-            Map<Object, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions);
+            MMap<Key, Holder<String>> managedVersions,
+            MMap<Key, Holder<String>> managedScopes,
+            MMap<Key, Holder<Boolean>> managedOptionals,
+            MMap<Key, Holder<String>> managedLocalPaths,
+            MMap<Key, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions);
 
     @Override
     public DependencyManager deriveChildManager(DependencyCollectionContext 
context) {
@@ -140,20 +138,20 @@ public abstract class AbstractDependencyManager 
implements DependencyManager {
             return this;
         }
 
-        Map<Object, Holder<String>> managedVersions = this.managedVersions;
-        Map<Object, Holder<String>> managedScopes = this.managedScopes;
-        Map<Object, Holder<Boolean>> managedOptionals = this.managedOptionals;
-        Map<Object, Holder<String>> managedLocalPaths = this.managedLocalPaths;
-        Map<Object, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions = this.managedExclusions;
+        MMap<Key, Holder<String>> managedVersions = this.managedVersions;
+        MMap<Key, Holder<String>> managedScopes = this.managedScopes;
+        MMap<Key, Holder<Boolean>> managedOptionals = this.managedOptionals;
+        MMap<Key, Holder<String>> managedLocalPaths = this.managedLocalPaths;
+        MMap<Key, Collection<Holder<Collection<Exclusion>>>> managedExclusions 
= this.managedExclusions;
 
         for (Dependency managedDependency : context.getManagedDependencies()) {
             Artifact artifact = managedDependency.getArtifact();
-            Object key = new Key(artifact);
+            Key key = new Key(artifact);
 
             String version = artifact.getVersion();
             if (!version.isEmpty() && !managedVersions.containsKey(key)) {
                 if (managedVersions == this.managedVersions) {
-                    managedVersions = new HashMap<>(this.managedVersions);
+                    managedVersions = MMap.copy(this.managedVersions);
                 }
                 managedVersions.put(key, new Holder<>(depth, version));
             }
@@ -161,7 +159,7 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
             String scope = managedDependency.getScope();
             if (!scope.isEmpty() && !managedScopes.containsKey(key)) {
                 if (managedScopes == this.managedScopes) {
-                    managedScopes = new HashMap<>(this.managedScopes);
+                    managedScopes = MMap.copy(this.managedScopes);
                 }
                 managedScopes.put(key, new Holder<>(depth, scope));
             }
@@ -169,7 +167,7 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
             Boolean optional = managedDependency.getOptional();
             if (optional != null && !managedOptionals.containsKey(key)) {
                 if (managedOptionals == this.managedOptionals) {
-                    managedOptionals = new HashMap<>(this.managedOptionals);
+                    managedOptionals = MMap.copy(this.managedOptionals);
                 }
                 managedOptionals.put(key, new Holder<>(depth, optional));
             }
@@ -179,7 +177,7 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
                     : 
systemDependencyScope.getSystemPath(managedDependency.getArtifact());
             if (localPath != null && !managedLocalPaths.containsKey(key)) {
                 if (managedLocalPaths == this.managedLocalPaths) {
-                    managedLocalPaths = new HashMap<>(this.managedLocalPaths);
+                    managedLocalPaths = MMap.copy(this.managedLocalPaths);
                 }
                 managedLocalPaths.put(key, new Holder<>(depth, localPath));
             }
@@ -187,22 +185,30 @@ public abstract class AbstractDependencyManager 
implements DependencyManager {
             Collection<Exclusion> exclusions = 
managedDependency.getExclusions();
             if (!exclusions.isEmpty()) {
                 if (managedExclusions == this.managedExclusions) {
-                    managedExclusions = new HashMap<>(this.managedExclusions);
+                    managedExclusions = MMap.copy(this.managedExclusions);
+                }
+                Collection<Holder<Collection<Exclusion>>> managed = 
managedExclusions.get(key);
+                if (managed == null) {
+                    managed = new ArrayList<>();
+                    managedExclusions.put(key, managed);
                 }
-                Collection<Holder<Collection<Exclusion>>> managed =
-                        managedExclusions.computeIfAbsent(key, k -> new 
ArrayList<>());
                 managed.add(new Holder<>(depth, exclusions));
             }
         }
 
-        return newInstance(managedVersions, managedScopes, managedOptionals, 
managedLocalPaths, managedExclusions);
+        return newInstance(
+                managedVersions.done(),
+                managedScopes.done(),
+                managedOptionals.done(),
+                managedLocalPaths.done(),
+                managedExclusions.done());
     }
 
     @Override
     public DependencyManagement manageDependency(Dependency dependency) {
         requireNonNull(dependency, "dependency cannot be null");
         DependencyManagement management = null;
-        Object key = new Key(dependency.getArtifact());
+        Key key = new Key(dependency.getArtifact());
 
         if (isApplied()) {
             Holder<String> version = managedVersions.get(key);
@@ -225,7 +231,7 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
                 if (systemDependencyScope != null
                         && !systemDependencyScope.is(scope.getValue())
                         && 
systemDependencyScope.getSystemPath(dependency.getArtifact()) != null) {
-                    Map<String, String> properties =
+                    HashMap<String, String> properties =
                             new 
HashMap<>(dependency.getArtifact().getProperties());
                     systemDependencyScope.setSystemPath(properties, null);
                     management.setProperties(properties);
@@ -242,7 +248,7 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
                     if (management == null) {
                         management = new DependencyManagement();
                     }
-                    Map<String, String> properties =
+                    HashMap<String, String> properties =
                             new 
HashMap<>(dependency.getArtifact().getProperties());
                     systemDependencyScope.setSystemPath(properties, 
localPath.getValue());
                     management.setProperties(properties);
@@ -320,6 +326,7 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
                 && managedVersions.equals(that.managedVersions)
                 && managedScopes.equals(that.managedScopes)
                 && managedOptionals.equals(that.managedOptionals)
+                && managedLocalPaths.equals(that.managedLocalPaths)
                 && managedExclusions.equals(that.managedExclusions);
     }
 
@@ -334,7 +341,8 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
 
         Key(Artifact artifact) {
             this.artifact = artifact;
-            this.hashCode = Objects.hash(artifact.getGroupId(), 
artifact.getArtifactId());
+            this.hashCode = Objects.hash(
+                    artifact.getArtifactId(), artifact.getGroupId(), 
artifact.getExtension(), artifact.getClassifier());
         }
 
         @Override
@@ -365,10 +373,12 @@ public abstract class AbstractDependencyManager 
implements DependencyManager {
     protected static class Holder<T> {
         private final int depth;
         private final T value;
+        private final int hashCode;
 
         Holder(int depth, T value) {
             this.depth = depth;
             this.value = requireNonNull(value);
+            this.hashCode = Objects.hash(depth, value);
         }
 
         public int getDepth() {
@@ -378,5 +388,19 @@ public abstract class AbstractDependencyManager implements 
DependencyManager {
         public T getValue() {
             return value;
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof Holder)) {
+                return false;
+            }
+            Holder<?> holder = (Holder<?>) o;
+            return depth == holder.depth && Objects.equals(value, 
holder.value);
+        }
+
+        @Override
+        public int hashCode() {
+            return hashCode;
+        }
     }
 }
diff --git 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/ClassicDependencyManager.java
 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/ClassicDependencyManager.java
index cdf82db5..baa4b7e3 100644
--- 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/ClassicDependencyManager.java
+++ 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/ClassicDependencyManager.java
@@ -19,7 +19,6 @@
 package org.eclipse.aether.util.graph.manager;
 
 import java.util.Collection;
-import java.util.Map;
 
 import org.eclipse.aether.collection.DependencyCollectionContext;
 import org.eclipse.aether.collection.DependencyManager;
@@ -67,11 +66,11 @@ public final class ClassicDependencyManager extends 
AbstractDependencyManager {
             int depth,
             int deriveUntil,
             int applyFrom,
-            Map<Object, Holder<String>> managedVersions,
-            Map<Object, Holder<String>> managedScopes,
-            Map<Object, Holder<Boolean>> managedOptionals,
-            Map<Object, Holder<String>> managedLocalPaths,
-            Map<Object, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions,
+            MMap<Key, Holder<String>> managedVersions,
+            MMap<Key, Holder<String>> managedScopes,
+            MMap<Key, Holder<Boolean>> managedOptionals,
+            MMap<Key, Holder<String>> managedLocalPaths,
+            MMap<Key, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions,
             SystemDependencyScope systemDependencyScope) {
         super(
                 depth,
@@ -98,11 +97,11 @@ public final class ClassicDependencyManager extends 
AbstractDependencyManager {
 
     @Override
     protected DependencyManager newInstance(
-            Map<Object, Holder<String>> managedVersions,
-            Map<Object, Holder<String>> managedScopes,
-            Map<Object, Holder<Boolean>> managedOptionals,
-            Map<Object, Holder<String>> managedLocalPaths,
-            Map<Object, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions) {
+            MMap<Key, Holder<String>> managedVersions,
+            MMap<Key, Holder<String>> managedScopes,
+            MMap<Key, Holder<Boolean>> managedOptionals,
+            MMap<Key, Holder<String>> managedLocalPaths,
+            MMap<Key, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions) {
         return new ClassicDependencyManager(
                 depth + 1,
                 deriveUntil,
diff --git 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DefaultDependencyManager.java
 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DefaultDependencyManager.java
index 9c1f8bb2..7ea86c05 100644
--- 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DefaultDependencyManager.java
+++ 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/DefaultDependencyManager.java
@@ -19,7 +19,6 @@
 package org.eclipse.aether.util.graph.manager;
 
 import java.util.Collection;
-import java.util.Map;
 
 import org.eclipse.aether.collection.DependencyManager;
 import org.eclipse.aether.graph.Exclusion;
@@ -58,11 +57,11 @@ public final class DefaultDependencyManager extends 
AbstractDependencyManager {
             int depth,
             int deriveUntil,
             int applyFrom,
-            Map<Object, Holder<String>> managedVersions,
-            Map<Object, Holder<String>> managedScopes,
-            Map<Object, Holder<Boolean>> managedOptionals,
-            Map<Object, Holder<String>> managedLocalPaths,
-            Map<Object, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions,
+            MMap<Key, Holder<String>> managedVersions,
+            MMap<Key, Holder<String>> managedScopes,
+            MMap<Key, Holder<Boolean>> managedOptionals,
+            MMap<Key, Holder<String>> managedLocalPaths,
+            MMap<Key, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions,
             SystemDependencyScope systemDependencyScope) {
         super(
                 depth,
@@ -78,11 +77,11 @@ public final class DefaultDependencyManager extends 
AbstractDependencyManager {
 
     @Override
     protected DependencyManager newInstance(
-            Map<Object, Holder<String>> managedVersions,
-            Map<Object, Holder<String>> managedScopes,
-            Map<Object, Holder<Boolean>> managedOptionals,
-            Map<Object, Holder<String>> managedLocalPaths,
-            Map<Object, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions) {
+            MMap<Key, Holder<String>> managedVersions,
+            MMap<Key, Holder<String>> managedScopes,
+            MMap<Key, Holder<Boolean>> managedOptionals,
+            MMap<Key, Holder<String>> managedLocalPaths,
+            MMap<Key, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions) {
         return new DefaultDependencyManager(
                 depth + 1,
                 deriveUntil,
diff --git 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/MMap.java
 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/MMap.java
new file mode 100644
index 00000000..8981a74d
--- /dev/null
+++ 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/MMap.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.eclipse.aether.util.graph.manager;
+
+import java.util.HashMap;
+
+/**
+ * Warning: this is a special map-like construct that suits only and should be 
used only in this package!
+ * It has the following properties:
+ * <ul>
+ *     <ul>memorizes once calculated hashCode</ul>
+ *     <ul>once hashCode calculated, goes into "read only" mode (put method 
will fail)</ul>
+ *     <ul>otherwise all the rest is same as for {@link HashMap}</ul>
+ * </ul>
+ *
+ * This class is not a generic map class; only those methods are "protected" 
that are in use in this very
+ * package.
+ *
+ * @param <K>
+ * @param <V>
+ */
+public class MMap<K, V> {
+    private static final MMap<?, ?> EMPTY_MAP = new MMap<>(new 
HashMap<>(0)).done();
+
+    @SuppressWarnings("unchecked")
+    public static <K, V> MMap<K, V> empty() {
+        return (MMap<K, V>) MMap.EMPTY_MAP;
+    }
+
+    public static <K, V> MMap<K, V> copy(MMap<K, V> orig) {
+        return new MMap<>(orig.delegate);
+    }
+
+    protected final HashMap<K, V> delegate;
+
+    private MMap(HashMap<K, V> delegate) {
+        this.delegate = new HashMap<>(delegate);
+    }
+
+    public boolean containsKey(K key) {
+        return delegate.containsKey(key);
+    }
+
+    public V get(K key) {
+        return delegate.get(key);
+    }
+
+    public V put(K key, V value) {
+        return delegate.put(key, value);
+    }
+
+    public MMap<K, V> done() {
+        return new DoneMMap<>(delegate);
+    }
+
+    @Override
+    public int hashCode() {
+        throw new IllegalStateException("MMap is not done yet");
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        throw new IllegalStateException("MMap is not done yet");
+    }
+
+    private static class DoneMMap<K, V> extends MMap<K, V> {
+        private final int hashCode;
+
+        private DoneMMap(HashMap<K, V> delegate) {
+            super(delegate);
+            this.hashCode = delegate.hashCode();
+        }
+
+        @Override
+        public V put(K key, V value) {
+            throw new IllegalStateException("Done MMap is immutable");
+        }
+
+        @Override
+        public MMap<K, V> done() {
+            return this;
+        }
+
+        @Override
+        public int hashCode() {
+            return hashCode;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof MMap)) {
+                return false;
+            }
+            MMap<?, ?> other = (MMap<?, ?>) o;
+            return delegate.equals(other.delegate);
+        }
+    }
+}
diff --git 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java
 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java
index e9c87c21..f49fb209 100644
--- 
a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java
+++ 
b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/manager/TransitiveDependencyManager.java
@@ -19,7 +19,6 @@
 package org.eclipse.aether.util.graph.manager;
 
 import java.util.Collection;
-import java.util.Map;
 
 import org.eclipse.aether.collection.DependencyManager;
 import org.eclipse.aether.graph.Exclusion;
@@ -55,11 +54,11 @@ public final class TransitiveDependencyManager extends 
AbstractDependencyManager
             int depth,
             int deriveUntil,
             int applyFrom,
-            Map<Object, Holder<String>> managedVersions,
-            Map<Object, Holder<String>> managedScopes,
-            Map<Object, Holder<Boolean>> managedOptionals,
-            Map<Object, Holder<String>> managedLocalPaths,
-            Map<Object, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions,
+            MMap<Key, Holder<String>> managedVersions,
+            MMap<Key, Holder<String>> managedScopes,
+            MMap<Key, Holder<Boolean>> managedOptionals,
+            MMap<Key, Holder<String>> managedLocalPaths,
+            MMap<Key, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions,
             SystemDependencyScope systemDependencyScope) {
         super(
                 depth,
@@ -75,11 +74,11 @@ public final class TransitiveDependencyManager extends 
AbstractDependencyManager
 
     @Override
     protected DependencyManager newInstance(
-            Map<Object, Holder<String>> managedVersions,
-            Map<Object, Holder<String>> managedScopes,
-            Map<Object, Holder<Boolean>> managedOptionals,
-            Map<Object, Holder<String>> managedLocalPaths,
-            Map<Object, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions) {
+            MMap<Key, Holder<String>> managedVersions,
+            MMap<Key, Holder<String>> managedScopes,
+            MMap<Key, Holder<Boolean>> managedOptionals,
+            MMap<Key, Holder<String>> managedLocalPaths,
+            MMap<Key, Collection<Holder<Collection<Exclusion>>>> 
managedExclusions) {
         return new TransitiveDependencyManager(
                 depth + 1,
                 deriveUntil,

Reply via email to