Skip to content

Commit 22a5caa

Browse files
committed
Improve Performance of getInterfaceAbstractMethods() for interfaces with many inherited methods
Refactor the implementation of getInterfaceAbstractMethods() - The Methods to be checked for overriding are now grouped by their name (MethodBinding.selector) - Replaced manually re-sized Arrays with ArrayLists
1 parent fa6f194 commit 22a5caa

File tree

1 file changed

+59
-59
lines changed

1 file changed

+59
-59
lines changed

org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java

Lines changed: 59 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,12 @@
5454

5555
import java.util.ArrayList;
5656
import java.util.Arrays;
57+
import java.util.Collection;
5758
import java.util.Comparator;
5859
import java.util.HashMap;
5960
import java.util.HashSet;
61+
import java.util.Iterator;
62+
import java.util.LinkedHashMap;
6063
import java.util.List;
6164
import java.util.Map;
6265
import java.util.Set;
@@ -2307,91 +2310,88 @@ public void detectWrapperResource() {
23072310
}
23082311

23092312
protected MethodBinding [] getInterfaceAbstractContracts(Scope scope, boolean replaceWildcards, boolean filterDefaultMethods) throws InvalidBindingException {
2310-
23112313
if (!isInterface() || !isValidBinding()) {
23122314
throw new InvalidBindingException("Not a functional interface"); //$NON-NLS-1$
23132315
}
23142316

2315-
MethodBinding [] methods = methods();
2316-
MethodBinding [] contracts = new MethodBinding[0];
2317-
int contractsCount = 0;
2318-
int contractsLength = 0;
2317+
MethodBinding [] methods = methods(); // All methods defined by THIS interface
2318+
Map<String, List<MethodBinding>> contractsBySelector = new LinkedHashMap<>();
23192319

2320+
// Fill List of contracts by name with all methods of super interfaces
23202321
ReferenceBinding [] superInterfaces = superInterfaces();
23212322
for (ReferenceBinding superInterface : superInterfaces) {
23222323
// filterDefaultMethods=false => keep default methods needed to filter out any abstract methods they may override:
23232324
MethodBinding [] superInterfaceContracts = superInterface.getInterfaceAbstractContracts(scope, replaceWildcards, false);
23242325
final int superInterfaceContractsLength = superInterfaceContracts == null ? 0 : superInterfaceContracts.length;
23252326
if (superInterfaceContractsLength == 0) continue;
2326-
if (contractsLength < contractsCount + superInterfaceContractsLength) {
2327-
System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsLength = contractsCount + superInterfaceContractsLength], 0, contractsCount);
2327+
for (MethodBinding superInterfaceContract : superInterfaceContracts) {
2328+
if (superInterfaceContract == null) {
2329+
continue;
2330+
}
2331+
contractsBySelector.computeIfAbsent(String.valueOf(superInterfaceContract.selector), key -> new ArrayList<>()).add(superInterfaceContract);
23282332
}
2329-
System.arraycopy(superInterfaceContracts, 0, contracts, contractsCount, superInterfaceContractsLength);
2330-
contractsCount += superInterfaceContractsLength;
23312333
}
23322334

23332335
LookupEnvironment environment = scope.environment();
2334-
for (int i = 0, length = methods == null ? 0 : methods.length; i < length; i++) {
2335-
final MethodBinding method = methods[i];
2336-
if (method == null || method.isStatic() || method.redeclaresPublicObjectMethod(scope) || method.isPrivate())
2337-
continue;
2338-
if (!method.isValidBinding())
2339-
throw new InvalidBindingException("Not a functional interface"); //$NON-NLS-1$
2340-
for (int j = 0; j < contractsCount;) {
2341-
if ( contracts[j] != null && MethodVerifier.doesMethodOverride(method, contracts[j], environment)) {
2342-
contractsCount--;
2343-
// abstract method from super type overridden by present interface ==> contracts[j] = null;
2344-
if (j < contractsCount) {
2345-
System.arraycopy(contracts, j+1, contracts, j, contractsCount - j);
2346-
continue;
2336+
if (methods != null) {
2337+
for (final MethodBinding method : methods) {
2338+
if (method == null || method.isStatic() || method.redeclaresPublicObjectMethod(scope) || method.isPrivate())
2339+
continue;
2340+
if (!method.isValidBinding())
2341+
throw new InvalidBindingException("Not a functional interface"); //$NON-NLS-1$
2342+
List<MethodBinding> contractsOfSelector = contractsBySelector.computeIfAbsent(String.valueOf(method.selector), key -> new ArrayList<>());
2343+
Iterator<MethodBinding> iterator = contractsOfSelector.iterator();
2344+
2345+
// Does 'method' override any super interface method?
2346+
while (iterator.hasNext()) {
2347+
MethodBinding superInterfaceContract = iterator.next();
2348+
2349+
if (MethodVerifier.doesMethodOverride(method, superInterfaceContract, environment)) {
2350+
iterator.remove();
23472351
}
23482352
}
2349-
j++;
2350-
}
2351-
if (filterDefaultMethods && method.isDefaultMethod())
2352-
continue; // skip default method itself
2353-
if (contractsCount == contractsLength) {
2354-
System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsLength += 16], 0, contractsCount);
2355-
}
2356-
if(environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
2357-
ImplicitNullAnnotationVerifier.ensureNullnessIsKnown(method, scope);
2353+
if (filterDefaultMethods && method.isDefaultMethod())
2354+
continue; // skip default method itself
2355+
if(environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
2356+
ImplicitNullAnnotationVerifier.ensureNullnessIsKnown(method, scope);
2357+
}
2358+
contractsOfSelector.add(method);
23582359
}
2359-
contracts[contractsCount++] = method;
23602360
}
2361+
23612362
// check mutual overriding of inherited methods (i.e., not from current type):
2362-
for (int i = 0; i < contractsCount; i++) {
2363-
MethodBinding contractI = contracts[i];
2364-
if (TypeBinding.equalsEquals(contractI.declaringClass, this))
2365-
continue;
2366-
for (int j = 0; j < contractsCount; j++) {
2367-
MethodBinding contractJ = contracts[j];
2368-
if (i == j || TypeBinding.equalsEquals(contractJ.declaringClass, this))
2363+
for (List<MethodBinding> contractList : contractsBySelector.values()) {
2364+
for (int i = 0; i < contractList.size(); ++i) {
2365+
MethodBinding contractI = contractList.get(i);
2366+
2367+
if (TypeBinding.equalsEquals(contractI.declaringClass, this)) {
23692368
continue;
2370-
if (contractI == contractJ || MethodVerifier.doesMethodOverride(contractI, contractJ, environment)) {
2371-
contractsCount--;
2372-
// abstract method from one super type overridden by other super interface ==> contracts[j] = null;
2373-
if (j < contractsCount) {
2374-
System.arraycopy(contracts, j+1, contracts, j, contractsCount - j);
2369+
}
2370+
2371+
for (int j = 0; j < contractList.size(); ++j) {
2372+
MethodBinding contractJ = contractList.get(j);
2373+
2374+
if (i == j || TypeBinding.equalsEquals(contractJ.declaringClass, this)) {
2375+
continue;
2376+
}
2377+
2378+
if (contractI == contractJ || MethodVerifier.doesMethodOverride(contractI, contractJ, environment)) {
2379+
contractList.remove(j);
2380+
2381+
--j;
2382+
if (j < i) {
2383+
--i;
2384+
}
23752385
}
2376-
j--;
2377-
if (j < i)
2378-
i--;
2379-
continue;
23802386
}
2381-
}
2382-
if (filterDefaultMethods && contractI.isDefaultMethod()) {
2383-
contractsCount--;
2384-
// remove default method after it has eliminated any matching abstract methods from contracts
2385-
if (i < contractsCount) {
2386-
System.arraycopy(contracts, i+1, contracts, i, contractsCount - i);
2387+
2388+
if (filterDefaultMethods && contractI.isDefaultMethod()) {
2389+
contractList.remove(i);
2390+
--i;
23872391
}
2388-
i--;
23892392
}
23902393
}
2391-
if (contractsCount < contractsLength) {
2392-
System.arraycopy(contracts, 0, contracts = new MethodBinding[contractsCount], 0, contractsCount);
2393-
}
2394-
return contracts;
2394+
return contractsBySelector.values().stream().flatMap(Collection::stream).toArray(MethodBinding[]::new);
23952395
}
23962396
@Override
23972397
public MethodBinding getSingleAbstractMethod(Scope scope, boolean replaceWildcards) {

0 commit comments

Comments
 (0)