Given a collection of classes, what\'s the best way to find the nearest common superclass?
E.g., given the following:
interface A {}
interface B {}
This is based on Adam's answer.
Firstly, I optimized getClasses
so that it will create fewer temporary objects, i.e. only one ArrayDeque
instead of a LinkedHashSet
for each level.
public static Set> getSuperclasses(Class> clazz) {
final Set> result = new LinkedHashSet<>();
final Queue> queue = new ArrayDeque<>();
queue.add(clazz);
if (clazz.isInterface()) {
queue.add(Object.class); // optional
}
while (!queue.isEmpty()) {
Class> c = queue.remove();
if (result.add(c)) {
Class> sup = c.getSuperclass();
if (sup != null) queue.add(sup);
queue.addAll(Arrays.asList(c.getInterfaces()));
}
}
return result;
}
To find the common superclasses, calls to retainAll(getClasses())
can be replaced with if (!isAssignableFrom()) remove()
, so that the quite expensive getClasses
will be called only once. This method looks like it has worse complexity than the original solution, because of the nested loops, but that's only becaus in the original solution, the inner loop is hidden in retainAll
.
public static Set> commonSuperclasses(Iterable> classes) {
Iterator> it = classes.iterator();
if (!it.hasNext()) {
return Collections.emptySet();
}
// begin with set from first hierarchy
Set> result = getSuperclasses(it.next());
// remove non-superclasses of remaining
while (it.hasNext()) {
Class> c = it.next();
Iterator> resultIt = result.iterator();
while (resultIt.hasNext()) {
Class> sup = resultIt.next();
if (!sup.isAssignableFrom(c)) {
resultIt.remove();
}
}
}
return result;
}
Finally, in your question it seems that you are only interested in the lowest superclass, which is why we use an ordered set. But we can also remove non-leaf classes easily. The complexity here is O(n) best-case (if there is only one result) and O(n^2) worst-case.
public static List> lowestCommonSuperclasses(Iterable> classes) {
Collection> commonSupers = commonSuperclasses(classes);
return lowestClasses(commonSupers);
}
public static List> lowestClasses(Collection> classes) {
final LinkedList> source = new LinkedList<>(classes);
final ArrayList> result = new ArrayList<>(classes.size());
while (!source.isEmpty()) {
Iterator> srcIt = source.iterator();
Class> c = srcIt.next();
srcIt.remove();
while (srcIt.hasNext()) {
Class> c2 = srcIt.next();
if (c2.isAssignableFrom(c)) {
srcIt.remove();
} else if (c.isAssignableFrom(c2)) {
c = c2;
srcIt.remove();
}
}
result.add(c);
}
result.trimToSize();
return result;
}