You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwebbeans.apache.org by ta...@apache.org on 2026/07/24 13:25:18 UTC

(openwebbeans) 02/04: speed up getNonPrivateMethods: drop O(n^2) List#contains, skip redundant per-level getMethods()

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

asf-gitbox-commits pushed a commit to branch startupperf
in repository https://gitbox.apache.org/repos/asf/openwebbeans.git

commit 50fd394b94d96237b062e43617f1c1a548a88223
Author: tandraschko <ta...@apache.org>
AuthorDate: Fri Jul 24 15:22:16 2026 +0200

    speed up getNonPrivateMethods: drop O(n^2) List#contains, skip redundant per-level getMethods()
---
 .../java/org/apache/webbeans/util/ClassUtil.java   | 43 +++++++++++++++++-----
 1 file changed, 34 insertions(+), 9 deletions(-)

diff --git a/webbeans-impl/src/main/java/org/apache/webbeans/util/ClassUtil.java b/webbeans-impl/src/main/java/org/apache/webbeans/util/ClassUtil.java
index 86d727c4d..80ffbe36f 100644
--- a/webbeans-impl/src/main/java/org/apache/webbeans/util/ClassUtil.java
+++ b/webbeans-impl/src/main/java/org/apache/webbeans/util/ClassUtil.java
@@ -293,22 +293,29 @@ public final class ClassUtil
     {
         Map<String, List<Method>> methodMap = new HashMap<>();
         List<Method> allMethods = new ArrayList<>(10);
+        // mirrors allMethods for O(1) membership checks (avoids O(n^2) List#contains with Method#equals)
+        Set<Method> allMethodsSet = new HashSet<>();
 
         Class<?> clazz = topClass;
 
         if (!clazz.isAnnotation() && clazz.isInterface())
         {
-            addNonPrivateMethods(topClass, excludeFinalMethods, methodMap, allMethods, clazz);
+            addNonPrivateMethods(topClass, excludeFinalMethods, methodMap, allMethods, allMethodsSet, clazz, true);
             for (Class<?> parent : clazz.getInterfaces())
             {
-                addNonPrivateMethods(topClass, excludeFinalMethods, methodMap, allMethods, parent);
+                addNonPrivateMethods(topClass, excludeFinalMethods, methodMap, allMethods, allMethodsSet, parent, true);
             }
         }
         else
         {
+            // topClass.getMethods() already returns every public method in the whole hierarchy, so only the
+            // top class needs that (expensive) call; superclasses only add their declared (non-public) methods.
+            // Public methods declared higher up are already collected and deduped via allMethodsSet.
+            boolean topLevel = true;
             while (clazz != null)
             {
-                addNonPrivateMethods(topClass, excludeFinalMethods, methodMap, allMethods, clazz);
+                addNonPrivateMethods(topClass, excludeFinalMethods, methodMap, allMethods, allMethodsSet, clazz, topLevel);
+                topLevel = false;
                 clazz = clazz.getSuperclass();
             }
         }
@@ -391,20 +398,36 @@ public final class ClassUtil
 
     private static void addNonPrivateMethods(Class<?> topClass, boolean excludeFinalMethods,
                                              Map<String, List<Method>> methodMap, List<Method> allMethods,
-                                             Class<?> clazz)
+                                             Set<Method> allMethodsSet, Class<?> clazz, boolean includeInheritedPublicMethods)
     {
-        List<Method> temp = new ArrayList<>(Arrays.asList(clazz.getMethods()));
-        for (Method method : clazz.getDeclaredMethods())
+        List<Method> temp;
+        if (includeInheritedPublicMethods)
         {
-            if (!temp.contains(method))
+            Method[] classMethods = clazz.getMethods();
+            Method[] declaredMethods = clazz.getDeclaredMethods();
+            temp = new ArrayList<>(classMethods.length + declaredMethods.length);
+            Collections.addAll(temp, classMethods);
+            for (Method method : declaredMethods)
             {
-                temp.add(method);
+                // a declared method is in getMethods() iff it is public, so only the non-public
+                // declared methods (private/protected/package) still need to be added here.
+                // This avoids both an O(n^2) List#contains and a temporary de-dup Set.
+                if (!Modifier.isPublic(method.getModifiers()))
+                {
+                    temp.add(method);
+                }
             }
         }
+        else
+        {
+            // inherited public methods were already collected from the top class (and deduped via
+            // allMethodsSet below), so this level only needs to contribute its declared methods.
+            temp = Arrays.asList(clazz.getDeclaredMethods());
+        }
 
         for (Method method : temp)
         {
-            if (allMethods.contains(method))
+            if (allMethodsSet.contains(method))
             {
                 continue;
             }
@@ -450,6 +473,7 @@ public final class ClassUtil
                 methods = new ArrayList<>();
                 methods.add(method);
                 allMethods.add(method);
+                allMethodsSet.add(method);
                 methodMap.put(method.getName(), methods);
             }
             else
@@ -459,6 +483,7 @@ public final class ClassUtil
                     // method is not overridden, so add it
                     methods.add(method);
                     allMethods.add(method);
+                    allMethodsSet.add(method);
                 }
             }
         }