Hi,

I have an enhancement proposal for java.lang.Class.methodToString and 
java.lang.Class.getTypeName.

First one is used when NoSuchMethodException is thrown from Class::getMethod 
which is in turn widely used in Spring Framework and often throws.

In current implementation we have 2 major problems:

- we create stream for the case when argTypes is not null but empty (which 
happens e. g. when Class::getMethod is called without vararg and throws)
- we have torn StringBuilder::append chain

I’ve modified the method to skip creation of Stream for empty array and used 
separate StringBuilder for each case. Latter allowed to rid SB completely and 
use invokedynamic-based concatenation.

I’ve compared both approaches for 2 cases:

1) argTypes is empty
2) argTypes.length == 1


Benchmark                                                   Mode  Cnt     Score 
    Error   Units

methodToString_noArgs                                       avgt   25   170,986 
±   5,708   ns/op
methodToString_noArgs_patched                               avgt   25    26,883 
±   2,906   ns/op

methodToString_1arg                                         avgt   25   183,012 
±   0,701   ns/op
methodToString_1arg_patched                                 avgt   25   112,784 
±   0,920   ns/op

methodToString_noArgs:·gc.alloc.rate.norm                   avgt   25   881,600 
±   9,786    B/op
methodToString_noArgs_patched:·gc.alloc.rate.norm           avgt   25   128,000 
±   0,001    B/op

methodToString_1arg:·gc.alloc.rate.norm                     avgt   25   960,000 
±   0,001    B/op
methodToString_1arg_patched:·gc.alloc.rate.norm             avgt   25   552,000 
±   0,001    B/op


We have the same problem regarding misusage of StringBuilder in Class:: 
getTypeName:

StringBuilder sb = new StringBuilder();
sb.append(cl.getName());
for (int i = 0; i < dimensions; i++) {
    sb.append("[]");
}
return sb.toString();

I suggest to use String::repeat instead of the loop: this again allows to get 
rid of StringBuilder and replace mentioned code with

return cl.getName() + "[]".repeat(dimensions);

Here are benchmark results executed for type Object[].class:

                                          Mode  Cnt     Score     Error   Units
getTypeName_patched                       avgt   25    16,037 ±   0,431   ns/op
getTypeName_patched:·gc.alloc.rate.norm   avgt   25    64,000 ±   0,001    B/op

getTypeName                               avgt   25    34,274 ±   1,432   ns/op
getTypeName:·gc.alloc.rate.norm           avgt   25   152,000 ±   0,001    B/op


Regards,
Sergei Tsypanov
diff --git a/src/java.base/share/classes/java/lang/Class.java b/src/java.base/share/classes/java/lang/Class.java
--- a/src/java.base/share/classes/java/lang/Class.java
+++ b/src/java.base/share/classes/java/lang/Class.java
@@ -276,8 +276,7 @@
                           collect(Collectors.joining(",", "<", ">")));
             }
 
-            for (int i = 0; i < arrayDepth; i++)
-                sb.append("[]");
+            sb.append("[]".repeat(arrayDepth));
 
             return sb.toString();
         }
@@ -1588,12 +1587,7 @@
                     dimensions++;
                     cl = cl.getComponentType();
                 } while (cl.isArray());
-                StringBuilder sb = new StringBuilder();
-                sb.append(cl.getName());
-                for (int i = 0; i < dimensions; i++) {
-                    sb.append("[]");
-                }
-                return sb.toString();
+                return cl.getName() + "[]".repeat(dimensions);
             } catch (Throwable e) { /*FALLTHRU*/ }
         }
         return getName();
@@ -3417,14 +3411,15 @@
      * Helper method to get the method name from arguments.
      */
     private String methodToString(String name, Class<?>[] argTypes) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(getName() + "." + name + "(");
-        if (argTypes != null) {
-            sb.append(Stream.of(argTypes).map(c -> {return (c == null) ? "null" : c.getName();}).
-                      collect(Collectors.joining(",")));
+        if (argTypes == null || argTypes.length == 0) {
+            return getName() + '.' + name + "()";
         }
-        sb.append(")");
-        return sb.toString();
+        return getName() + '.' + name
+                + '('
+                + Arrays.stream(argTypes)
+                        .map(c -> c == null ? "null" : c.getName())
+                        .collect(Collectors.joining(","))
+                + ')';
     }
 
     /** use serialVersionUID from JDK 1.1 for interoperability */

Reply via email to