鉴于一组类,找到最近的公共超类的最佳方法是什么?
例如,给出以下内容:
interface A {}
interface B {}
interface AB extends A, B {}
interface C {}
class AImpl implements A {}
class ABImpl implements AB {}
class ABImpl2 implements A, B {}
class BCImpl implements B, C {}
我希望以下(不详尽):
commonSuperclass(A, AImpl) == A
commonSuperclass(A, B, C) == Object or null, I'm not picky
commonSuperclass(A, AB) == A
commonSuperclass(AImpl, ABImpl) == A
commonSuperclass(ABImpl, ABImpl2) == either A or B or both, I'm not picky
commonSuperclass(AImpl, ABImpl, ABImpl2) == A
commonSuperclass(ABImpl, ABImpl2, BCImpl) == B
commonSuperclass(AImpl, ABImpl, ABImpl2, BCImpl) == Object
我想我最终可能会解决这个问题,但有人必须已经解决了Arrays.asList(...)
中的类型推断等问题。有人能指出我的算法,或者更好的是,现有的一些实用程序代码吗?
ETA:我了解反射API。这是我正在寻找的算法(或这种算法的实现)。
ETA:我知道这是DAG。谢谢。你很聪明。
ETA:关于拓扑排序(在re EJP's answer中):我熟悉的拓扑排序算法要求您:
n
开始,没有传入边缘(即,在这种情况下,大概是Object
和所有没有超级接口的接口 - 哪一个必须检查整个集合,加上所有超类/超接口,收集)并处理所有边(n, m)
(即所有m extends/implements n
,再次需要检查整个集合的信息),或m
开始,没有传出边(即,在这种情况下,所有类/接口m
都没有类k extends/implements m
,这也是检查整个集合以收集)并处理所有边(n, m)
(即所有类/接口m
扩展/实现 - 我们拥有哪些信息。)这些多次通过算法中的一种或另一种(好的,可能是#2)是最有效的方法,但它肯定不是显而易见的。它也完全有可能是我不熟悉的单程拓扑排序算法,或者我只是简单地得到了这些算法,但在这种情况下,再次,“它基本上是一种拓扑排序”并不会立即导致一个答案。
答案 0 :(得分:30)
据我所知,完整的解决方案
代码
private static Set<Class<?>> getClassesBfs(Class<?> clazz) {
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
Set<Class<?>> nextLevel = new LinkedHashSet<Class<?>>();
nextLevel.add(clazz);
do {
classes.addAll(nextLevel);
Set<Class<?>> thisLevel = new LinkedHashSet<Class<?>>(nextLevel);
nextLevel.clear();
for (Class<?> each : thisLevel) {
Class<?> superClass = each.getSuperclass();
if (superClass != null && superClass != Object.class) {
nextLevel.add(superClass);
}
for (Class<?> eachInt : each.getInterfaces()) {
nextLevel.add(eachInt);
}
}
} while (!nextLevel.isEmpty());
return classes;
}
private static List<Class<?>> commonSuperClass(Class<?>... classes) {
// start off with set from first hierarchy
Set<Class<?>> rollingIntersect = new LinkedHashSet<Class<?>>(
getClassesBfs(classes[0]));
// intersect with next
for (int i = 1; i < classes.length; i++) {
rollingIntersect.retainAll(getClassesBfs(classes[i]));
}
return new LinkedList<Class<?>>(rollingIntersect);
}
支持方法和测试
private static void test(Class<?>... classes) {
System.out.println("Common ancestor for "
+ simpleClassList(Arrays.asList(classes)) + ", Result => "
+ simpleClassList(commonSuperClass(classes)));
}
private static String simpleClassList(Collection<Class<?>> classes) {
StringBuilder builder = new StringBuilder();
for (Class<?> clazz : classes) {
builder.append(clazz.getSimpleName());
builder.append(",");
}
return builder.toString();
}
public static void main(String[] args) {
test(A.class, AImpl.class);
test(A.class, B.class, C.class);
test(A.class, AB.class);
test(AImpl.class, ABImpl.class);
test(ABImpl.class, ABImpl2.class);
test(AImpl.class, ABImpl.class, ABImpl2.class);
test(ABImpl.class, ABImpl2.class, BCImpl.class);
test(AImpl.class, ABImpl.class, ABImpl2.class, BCImpl.class);
test(AB.class, ABImpl.class);
}
输出
Common ancestor for A,AImpl,, Result => A,
Common ancestor for A,B,C,, Result =>
Common ancestor for A,AB,, Result => A,
Common ancestor for AImpl,ABImpl,, Result => A,
Common ancestor for ABImpl,ABImpl2,, Result => A,B,
Common ancestor for AImpl,ABImpl,ABImpl2,, Result => A,
Common ancestor for ABImpl,ABImpl2,BCImpl,, Result => B,
Common ancestor for AImpl,ABImpl,ABImpl2,BCImpl,, Result =>
Common ancestor for AB,ABImpl,, Result => AB,A,B,
答案 1 :(得分:8)
这是基于亚当的答案。
首先,我对getClasses
进行了优化,以便创建更少的临时对象,即每个级别只有一个ArrayDeque
而不是LinkedHashSet
。
public static Set<Class<?>> getSuperclasses(Class<?> clazz) {
final Set<Class<?>> result = new LinkedHashSet<>();
final Queue<Class<?>> 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;
}
要查找常用超类,可以使用retainAll(getClasses())
替换对if (!isAssignableFrom()) remove()
的调用,以便只调用一次非常昂贵的getClasses
。这个方法看起来比原始解决方案更复杂,因为嵌套循环,但这只是因为在原始解决方案中,内部循环隐藏在retainAll
中。
public static Set<Class<?>> commonSuperclasses(Iterable<Class<?>> classes) {
Iterator<Class<?>> it = classes.iterator();
if (!it.hasNext()) {
return Collections.emptySet();
}
// begin with set from first hierarchy
Set<Class<?>> result = getSuperclasses(it.next());
// remove non-superclasses of remaining
while (it.hasNext()) {
Class<?> c = it.next();
Iterator<Class<?>> resultIt = result.iterator();
while (resultIt.hasNext()) {
Class<?> sup = resultIt.next();
if (!sup.isAssignableFrom(c)) {
resultIt.remove();
}
}
}
return result;
}
最后,在你的问题中,似乎你只对最低的超类感兴趣,这就是我们使用有序集的原因。但我们也可以轻松删除非叶类。这里的复杂性是O(n)最佳情况(如果只有一个结果)和O(n ^ 2)最坏情况。
public static List<Class<?>> lowestCommonSuperclasses(Iterable<Class<?>> classes) {
Collection<Class<?>> commonSupers = commonSuperclasses(classes);
return lowestClasses(commonSupers);
}
public static List<Class<?>> lowestClasses(Collection<Class<?>> classes) {
final LinkedList<Class<?>> source = new LinkedList<>(classes);
final ArrayList<Class<?>> result = new ArrayList<>(classes.size());
while (!source.isEmpty()) {
Iterator<Class<?>> 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;
}
答案 2 :(得分:2)
试试这个。
static <T> Class<T> commonSuperClass(Class<? extends T> c1, Class<? extends T> c2, T... args) {
return (Class<T>)args.getClass().getComponentType();
}
static <T> Class<T> commonSuperClass(Class<? extends T> c1, Class<? extends T> c2, Class<? extends T> c3, T... args) {
return (Class<T>)args.getClass().getComponentType();
}
static <T> Class<T> commonSuperClass(Class<? extends T> c1, Class<? extends T> c2, Class<? extends T> c3, Class<? extends T> c4, T... args) {
return (Class<T>)args.getClass().getComponentType();
}
结果
System.out.println(commonSuperClass(A.class, AImpl.class)); // -> A
System.out.println(commonSuperClass(A.class, B.class, C.class)); // -> Object
System.out.println(commonSuperClass(A.class, AB.class)); // -> A
System.out.println(commonSuperClass(AImpl.class, ABImpl.class)); // -> A
System.out.println(commonSuperClass(ABImpl.class, ABImpl2.class)); // -> A
System.out.println(commonSuperClass(AImpl.class, ABImpl.class, ABImpl2.class)); // -> A
System.out.println(commonSuperClass(ABImpl.class, ABImpl2.class, BCImpl.class)); // -> B
System.out.println(commonSuperClass(AImpl.class, ABImpl.class, ABImpl2.class, BCImpl.class)); // -> Object
答案 3 :(得分:2)
对于Spring Framework用户,有0.7751083469484001 0.5140565759502351
。
E.g:
org.springframework.util.ClassUtils#determineCommonAncestor
答案 4 :(得分:0)
反射可以为您提供编写自己的代码所需的信息:
Class<?> c = SomeType.class; // or someObject.getClass()
Class<?> superClass = s.getSuperClass();
Class<?>[] interfaces = s.getInterfaces();
答案 5 :(得分:0)
我现在也面临这个问题。我只需要超类级别的答案。基本上我发现最好只做O(n)步行。
您必须定义一个操作Cmin(A,B),为您提供最近的A类和B类超类。
由于Cmin会导致类本身,因此您可以使用此算法:
结果= Cmin Ai | (ai elementIn classes)。
给你O(n)。
但请记住,Java在接口或类的类型上有所区别。