Skip to content

Commit dd83c8c

Browse files
committed
Reduce memory footprint of ReflectionUtils
1 parent 5d07855 commit dd83c8c

File tree

1 file changed

+74
-65
lines changed

1 file changed

+74
-65
lines changed

junit-platform-commons/src/main/java/org/junit/platform/commons/util/ReflectionUtils.java

+74-65
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import static java.lang.String.format;
1414
import static java.util.Collections.synchronizedMap;
1515
import static java.util.stream.Collectors.toCollection;
16-
import static java.util.stream.Collectors.toList;
1716
import static java.util.stream.Collectors.toSet;
1817
import static org.apiguardian.api.API.Status.DEPRECATED;
1918
import static org.apiguardian.api.API.Status.INTERNAL;
@@ -44,6 +43,7 @@
4443
import java.util.ArrayList;
4544
import java.util.Arrays;
4645
import java.util.Collections;
46+
import java.util.Comparator;
4747
import java.util.HashMap;
4848
import java.util.IdentityHashMap;
4949
import java.util.LinkedHashSet;
@@ -1367,14 +1367,14 @@ private static void detectInnerClassCycle(Class<?> clazz) {
13671367
public static <T> Constructor<T> getDeclaredConstructor(Class<T> clazz) {
13681368
Preconditions.notNull(clazz, "Class must not be null");
13691369
try {
1370-
List<Constructor<?>> constructors = Arrays.stream(clazz.getDeclaredConstructors())//
1370+
Constructor<?>[] constructors = Arrays.stream(clazz.getDeclaredConstructors())//
13711371
.filter(ctor -> !ctor.isSynthetic())//
1372-
.collect(toList());
1372+
.toArray(Constructor[]::new);
13731373

1374-
Preconditions.condition(constructors.size() == 1,
1374+
Preconditions.condition(constructors.length == 1,
13751375
() -> String.format("Class [%s] must declare a single constructor", clazz.getName()));
13761376

1377-
return (Constructor<T>) constructors.get(0);
1377+
return (Constructor<T>) constructors[0];
13781378
}
13791379
catch (Throwable t) {
13801380
throw ExceptionUtils.throwAsUncheckedException(getUnderlyingCause(t));
@@ -1444,26 +1444,26 @@ private static List<Field> findAllFieldsInHierarchy(Class<?> clazz, HierarchyTra
14441444
Preconditions.notNull(traversalMode, "HierarchyTraversalMode must not be null");
14451445

14461446
// @formatter:off
1447-
List<Field> localFields = getDeclaredFields(clazz).stream()
1447+
Field[] localFields = getDeclaredFields(clazz).stream()
14481448
.filter(field -> !field.isSynthetic())
1449-
.collect(toList());
1450-
List<Field> superclassFields = getSuperclassFields(clazz, traversalMode).stream()
1451-
.filter(field -> !isFieldShadowedByLocalFields(field, localFields))
1452-
.collect(toList());
1453-
List<Field> interfaceFields = getInterfaceFields(clazz, traversalMode).stream()
1454-
.filter(field -> !isFieldShadowedByLocalFields(field, localFields))
1455-
.collect(toList());
1449+
.toArray(Field[]::new);
1450+
Field[] superclassFields = getSuperclassFields(clazz, traversalMode).stream()
1451+
.filter(field -> isNotShadowedByLocalFields(field, localFields))
1452+
.toArray(Field[]::new);
1453+
Field[] interfaceFields = getInterfaceFields(clazz, traversalMode).stream()
1454+
.filter(field -> isNotShadowedByLocalFields(field, localFields))
1455+
.toArray(Field[]::new);
14561456
// @formatter:on
14571457

1458-
List<Field> fields = new ArrayList<>();
1458+
List<Field> fields = new ArrayList<>(superclassFields.length + interfaceFields.length + localFields.length);
14591459
if (traversalMode == TOP_DOWN) {
1460-
fields.addAll(superclassFields);
1461-
fields.addAll(interfaceFields);
1460+
Collections.addAll(fields, superclassFields);
1461+
Collections.addAll(fields, interfaceFields);
14621462
}
1463-
fields.addAll(localFields);
1463+
Collections.addAll(fields, localFields);
14641464
if (traversalMode == BOTTOM_UP) {
1465-
fields.addAll(interfaceFields);
1466-
fields.addAll(superclassFields);
1465+
Collections.addAll(fields, interfaceFields);
1466+
Collections.addAll(fields, superclassFields);
14671467
}
14681468
return fields;
14691469
}
@@ -1735,26 +1735,27 @@ private static List<Method> findAllMethodsInHierarchy(Class<?> clazz, HierarchyT
17351735
Preconditions.notNull(traversalMode, "HierarchyTraversalMode must not be null");
17361736

17371737
// @formatter:off
1738-
List<Method> localMethods = getDeclaredMethods(clazz, traversalMode).stream()
1738+
Method[] localMethods = getDeclaredMethods(clazz, traversalMode).stream()
17391739
.filter(method -> !method.isSynthetic())
1740-
.collect(toList());
1741-
List<Method> superclassMethods = getSuperclassMethods(clazz, traversalMode).stream()
1742-
.filter(method -> !isMethodOverriddenByLocalMethods(method, localMethods))
1743-
.collect(toList());
1744-
List<Method> interfaceMethods = getInterfaceMethods(clazz, traversalMode).stream()
1745-
.filter(method -> !isMethodOverriddenByLocalMethods(method, localMethods))
1746-
.collect(toList());
1740+
.toArray(Method[]::new);
1741+
Method[] superclassMethods = getSuperclassMethods(clazz, traversalMode).stream()
1742+
.filter(method -> isNotOverriddenByLocalMethods(method, localMethods))
1743+
.toArray(Method[]::new);
1744+
Method[] interfaceMethods = getInterfaceMethods(clazz, traversalMode).stream()
1745+
.filter(method -> isNotOverriddenByLocalMethods(method, localMethods))
1746+
.toArray(Method[]::new);
17471747
// @formatter:on
17481748

1749-
List<Method> methods = new ArrayList<>();
1749+
List<Method> methods = new ArrayList<>(
1750+
superclassMethods.length + interfaceMethods.length + localMethods.length);
17501751
if (traversalMode == TOP_DOWN) {
1751-
methods.addAll(superclassMethods);
1752-
methods.addAll(interfaceMethods);
1752+
Collections.addAll(methods, superclassMethods);
1753+
Collections.addAll(methods, interfaceMethods);
17531754
}
1754-
methods.addAll(localMethods);
1755+
Collections.addAll(methods, localMethods);
17551756
if (traversalMode == BOTTOM_UP) {
1756-
methods.addAll(interfaceMethods);
1757-
methods.addAll(superclassMethods);
1757+
Collections.addAll(methods, interfaceMethods);
1758+
Collections.addAll(methods, superclassMethods);
17581759
}
17591760
return methods;
17601761
}
@@ -1835,21 +1836,18 @@ private static List<Method> getDefaultMethods(Class<?> clazz) {
18351836
}
18361837

18371838
private static List<Field> toSortedMutableList(Field[] fields) {
1838-
// @formatter:off
1839-
return Arrays.stream(fields)
1840-
.sorted(ReflectionUtils::defaultFieldSorter)
1841-
// Use toCollection() instead of toList() to ensure list is mutable.
1842-
.collect(toCollection(ArrayList::new));
1843-
// @formatter:on
1839+
return toSortedMutableList(fields, ReflectionUtils::defaultFieldSorter);
18441840
}
18451841

18461842
private static List<Method> toSortedMutableList(Method[] methods) {
1847-
// @formatter:off
1848-
return Arrays.stream(methods)
1849-
.sorted(ReflectionUtils::defaultMethodSorter)
1850-
// Use toCollection() instead of toList() to ensure list is mutable.
1851-
.collect(toCollection(ArrayList::new));
1852-
// @formatter:on
1843+
return toSortedMutableList(methods, ReflectionUtils::defaultMethodSorter);
1844+
}
1845+
1846+
private static <T> List<T> toSortedMutableList(T[] items, Comparator<? super T> comparator) {
1847+
List<T> result = new ArrayList<>(items.length);
1848+
Collections.addAll(result, items);
1849+
result.sort(comparator);
1850+
return result;
18531851
}
18541852

18551853
/**
@@ -1882,21 +1880,21 @@ private static List<Method> getInterfaceMethods(Class<?> clazz, HierarchyTravers
18821880
for (Class<?> ifc : clazz.getInterfaces()) {
18831881

18841882
// @formatter:off
1885-
List<Method> localInterfaceMethods = getMethods(ifc).stream()
1883+
Method[] localInterfaceMethods = getMethods(ifc).stream()
18861884
.filter(m -> !isAbstract(m))
1887-
.collect(toList());
1885+
.toArray(Method[]::new);
18881886

1889-
List<Method> superinterfaceMethods = getInterfaceMethods(ifc, traversalMode).stream()
1890-
.filter(method -> !isMethodOverriddenByLocalMethods(method, localInterfaceMethods))
1891-
.collect(toList());
1887+
Method[] superinterfaceMethods = getInterfaceMethods(ifc, traversalMode).stream()
1888+
.filter(method -> isNotOverriddenByLocalMethods(method, localInterfaceMethods))
1889+
.toArray(Method[]::new);
18921890
// @formatter:on
18931891

18941892
if (traversalMode == TOP_DOWN) {
1895-
allInterfaceMethods.addAll(superinterfaceMethods);
1893+
Collections.addAll(allInterfaceMethods, superinterfaceMethods);
18961894
}
1897-
allInterfaceMethods.addAll(localInterfaceMethods);
1895+
Collections.addAll(allInterfaceMethods, localInterfaceMethods);
18981896
if (traversalMode == BOTTOM_UP) {
1899-
allInterfaceMethods.addAll(superinterfaceMethods);
1897+
Collections.addAll(allInterfaceMethods, superinterfaceMethods);
19001898
}
19011899
}
19021900
return allInterfaceMethods;
@@ -1905,20 +1903,21 @@ private static List<Method> getInterfaceMethods(Class<?> clazz, HierarchyTravers
19051903
private static List<Field> getInterfaceFields(Class<?> clazz, HierarchyTraversalMode traversalMode) {
19061904
List<Field> allInterfaceFields = new ArrayList<>();
19071905
for (Class<?> ifc : clazz.getInterfaces()) {
1908-
List<Field> localInterfaceFields = getFields(ifc);
1906+
Field[] localInterfaceFields = ifc.getFields();
1907+
Arrays.sort(localInterfaceFields, ReflectionUtils::defaultFieldSorter);
19091908

19101909
// @formatter:off
1911-
List<Field> superinterfaceFields = getInterfaceFields(ifc, traversalMode).stream()
1912-
.filter(field -> !isFieldShadowedByLocalFields(field, localInterfaceFields))
1913-
.collect(toList());
1910+
Field[] superinterfaceFields = getInterfaceFields(ifc, traversalMode).stream()
1911+
.filter(field -> isNotShadowedByLocalFields(field, localInterfaceFields))
1912+
.toArray(Field[]::new);
19141913
// @formatter:on
19151914

19161915
if (traversalMode == TOP_DOWN) {
1917-
allInterfaceFields.addAll(superinterfaceFields);
1916+
Collections.addAll(allInterfaceFields, superinterfaceFields);
19181917
}
1919-
allInterfaceFields.addAll(localInterfaceFields);
1918+
Collections.addAll(allInterfaceFields, localInterfaceFields);
19201919
if (traversalMode == BOTTOM_UP) {
1921-
allInterfaceFields.addAll(superinterfaceFields);
1920+
Collections.addAll(allInterfaceFields, superinterfaceFields);
19221921
}
19231922
}
19241923
return allInterfaceFields;
@@ -1932,11 +1931,16 @@ private static List<Field> getSuperclassFields(Class<?> clazz, HierarchyTraversa
19321931
return findAllFieldsInHierarchy(superclass, traversalMode);
19331932
}
19341933

1935-
private static boolean isFieldShadowedByLocalFields(Field field, List<Field> localFields) {
1934+
private static boolean isNotShadowedByLocalFields(Field field, Field[] localFields) {
19361935
if (useLegacySearchSemantics) {
1937-
return localFields.stream().anyMatch(local -> local.getName().equals(field.getName()));
1936+
for (Field local : localFields) {
1937+
if (local.getName().equals(field.getName())) {
1938+
return false;
1939+
}
1940+
}
1941+
return true;
19381942
}
1939-
return false;
1943+
return true;
19401944
}
19411945

19421946
private static List<Method> getSuperclassMethods(Class<?> clazz, HierarchyTraversalMode traversalMode) {
@@ -1947,8 +1951,13 @@ private static List<Method> getSuperclassMethods(Class<?> clazz, HierarchyTraver
19471951
return findAllMethodsInHierarchy(superclass, traversalMode);
19481952
}
19491953

1950-
private static boolean isMethodOverriddenByLocalMethods(Method method, List<Method> localMethods) {
1951-
return localMethods.stream().anyMatch(local -> isMethodOverriddenBy(method, local));
1954+
private static boolean isNotOverriddenByLocalMethods(Method method, Method[] localMethods) {
1955+
for (Method local : localMethods) {
1956+
if (isMethodOverriddenBy(method, local)) {
1957+
return false;
1958+
}
1959+
}
1960+
return true;
19521961
}
19531962

19541963
private static boolean isMethodOverriddenBy(Method upper, Method lower) {

0 commit comments

Comments
 (0)