diff --git a/impl/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java b/impl/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java index 5f20f38e594c..4c2e72e33541 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java +++ b/impl/maven-core/src/main/java/org/apache/maven/graph/DefaultGraphBuilder.java @@ -157,8 +157,7 @@ private List trimProjectsToRequest( if (request.getPom() != null) { result = getProjectsInRequestScope(request, activeProjects); - List sortedProjects = graph.getSortedProjects(); - result.sort(comparing(sortedProjects::indexOf)); + result.sort(comparing(buildProjectIndexMap(graph.getSortedProjects())::get)); result = includeAlsoMakeTransitively(result, request, graph); } @@ -189,8 +188,7 @@ private List trimSelectedProjects( result = includeAlsoMakeTransitively(result, request, graph); // Order the new list in the original order - List sortedProjects = graph.getSortedProjects(); - result.sort(comparing(sortedProjects::indexOf)); + result.sort(comparing(buildProjectIndexMap(graph.getSortedProjects())::get)); } } @@ -290,13 +288,24 @@ private List includeAlsoMakeTransitively( result = new ArrayList<>(projectsSet); // Order the new list in the original order - List sortedProjects = graph.getSortedProjects(); - result.sort(comparing(sortedProjects::indexOf)); + result.sort(comparing(buildProjectIndexMap(graph.getSortedProjects())::get)); } return result; } + /** + * Builds a Map from MavenProject to its index in the sorted list, enabling O(1) index lookups + * for sorting instead of O(n) ArrayList.indexOf() scans that cause O(n² log n) sort performance. + */ + private static Map buildProjectIndexMap(List sortedProjects) { + Map indexMap = new HashMap<>(sortedProjects.size() * 2); + for (int i = 0; i < sortedProjects.size(); i++) { + indexMap.put(sortedProjects.get(i), i); + } + return indexMap; + } + private void enrichRequestFromResumptionData(List projects, MavenExecutionRequest request) { if (request.isResume()) { projects.stream() diff --git a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/PhaseComparator.java b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/PhaseComparator.java index 2a1efccfbfbf..575c70ab03c7 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/PhaseComparator.java +++ b/impl/maven-core/src/main/java/org/apache/maven/lifecycle/internal/PhaseComparator.java @@ -19,16 +19,19 @@ package org.apache.maven.lifecycle.internal; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Compares phases within the context of a specific lifecycle with secondary sorting based on the {@link PhaseId}. */ public class PhaseComparator implements Comparator { /** - * The lifecycle phase ordering. + * Map from phase name to its index in the lifecycle, enabling O(1) lookups + * instead of O(n) List.indexOf() scans on every comparison. */ - private final List lifecyclePhases; + private final Map phaseIndexMap; /** * Constructor. @@ -36,24 +39,27 @@ public class PhaseComparator implements Comparator { * @param lifecyclePhases the lifecycle phase ordering. */ public PhaseComparator(List lifecyclePhases) { - this.lifecyclePhases = lifecyclePhases; + this.phaseIndexMap = new HashMap<>(lifecyclePhases.size() * 2); + for (int i = 0; i < lifecyclePhases.size(); i++) { + phaseIndexMap.put(lifecyclePhases.get(i), i); + } } @Override public int compare(String o1, String o2) { PhaseId p1 = PhaseId.of(o1); PhaseId p2 = PhaseId.of(o2); - int i1 = lifecyclePhases.indexOf(p1.executionPoint().prefix() + p1.phase()); - int i2 = lifecyclePhases.indexOf(p2.executionPoint().prefix() + p2.phase()); - if (i1 == -1 && i2 == -1) { + Integer i1 = phaseIndexMap.get(p1.executionPoint().prefix() + p1.phase()); + Integer i2 = phaseIndexMap.get(p2.executionPoint().prefix() + p2.phase()); + if (i1 == null && i2 == null) { // unknown phases, leave in existing order return 0; } - if (i1 == -1) { + if (i1 == null) { // second one is known, so it comes first return 1; } - if (i2 == -1) { + if (i2 == null) { // first one is known, so it comes first return -1; } diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java index bec34fe0fa76..6295ae92f3ee 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelObjectPool.java @@ -60,6 +60,7 @@ public class DefaultModelObjectPool implements ModelObjectProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultModelObjectPool.class); private final Map properties; + private final Set pooledTypes; public DefaultModelObjectPool() { this(System.getProperties()); @@ -67,6 +68,7 @@ public DefaultModelObjectPool() { DefaultModelObjectPool(Map properties) { this.properties = properties; + this.pooledTypes = parsePooledTypes(properties); } @Override @@ -79,8 +81,8 @@ public T process(T object) { Class objectType = object.getClass(); String simpleClassName = objectType.getSimpleName(); - // Check if this object type should be pooled (read configuration dynamically) - if (!getPooledTypes(properties).contains(simpleClassName)) { + // Check if this object type should be pooled + if (!pooledTypes.contains(simpleClassName)) { return object; } @@ -96,14 +98,16 @@ private String getProperty(String name, String defaultValue) { } /** - * Gets the set of object types that should be pooled. + * Parses the set of object types that should be pooled from properties. + * Called once at construction time to avoid re-parsing on every {@link #process} call. */ - private Set getPooledTypes(Map properties) { - String pooledTypesProperty = getProperty(Constants.MAVEN_MODEL_PROCESSOR_POOLED_TYPES, "Dependency"); + private static Set parsePooledTypes(Map properties) { + Object value = properties.get(Constants.MAVEN_MODEL_PROCESSOR_POOLED_TYPES); + String pooledTypesProperty = value instanceof String str ? str : "Dependency"; return Arrays.stream(pooledTypesProperty.split(",")) .map(String::trim) .filter(s -> !s.isEmpty()) - .collect(Collectors.toSet()); + .collect(Collectors.toUnmodifiableSet()); } /** @@ -199,6 +203,9 @@ public boolean equals(Object obj) { if (!(obj instanceof PoolKey other)) { return false; } + if (hashCode != other.hashCode) { + return false; + } return objectsEqual(object, other.object); } @@ -283,21 +290,23 @@ private static int computeHashCode(Object obj) { /** * Custom hash code for Dependency objects based on all fields. + * Inlined to avoid the Object[] varargs allocation from Objects.hash(). */ private static int dependencyHashCode(org.apache.maven.api.model.Dependency dep) { - return Objects.hash( - dep.getGroupId(), - dep.getArtifactId(), - dep.getVersion(), - dep.getType(), - dep.getClassifier(), - dep.getScope(), - dep.getSystemPath(), - dep.getExclusions(), - dep.getOptional(), - dep.getLocationKeys(), - locationsHashCode(dep), - dep.getImportedFrom()); + int h = 1; + h = 31 * h + Objects.hashCode(dep.getGroupId()); + h = 31 * h + Objects.hashCode(dep.getArtifactId()); + h = 31 * h + Objects.hashCode(dep.getVersion()); + h = 31 * h + Objects.hashCode(dep.getType()); + h = 31 * h + Objects.hashCode(dep.getClassifier()); + h = 31 * h + Objects.hashCode(dep.getScope()); + h = 31 * h + Objects.hashCode(dep.getSystemPath()); + h = 31 * h + Objects.hashCode(dep.getExclusions()); + h = 31 * h + Objects.hashCode(dep.getOptional()); + h = 31 * h + Objects.hashCode(dep.getLocationKeys()); + h = 31 * h + locationsHashCode(dep); + h = 31 * h + Objects.hashCode(dep.getImportedFrom()); + return h; } /**