Finding the nearest common superclass (or superinterface) of a collection of classes

后端 未结 6 1834
青春惊慌失措
青春惊慌失措 2020-12-08 14:23

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 {}
         


        
6条回答
  •  野趣味
    野趣味 (楼主)
    2020-12-08 14:47

    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;
    } 
    

提交回复
热议问题