【问题标题】:How to sort classes according to their inheritance level如何根据继承级别对类进行排序
【发布时间】:2018-06-19 06:23:24
【问题描述】:

请创建一个算法,该算法采用类列表并以某种方式对它们进行排序,无论何时

  1. A 类是 B 类的子类

  2. A 类实现接口 B

B的索引小于A。

到目前为止,我尝试的是,

public void sortClasses(Class... classes) {
        List<Class> classesToSort = new ArrayList<>();
        for(Class c : classes) {
            Class superClass = c.getSuperclass();
            if(superClass != null) {
                classesToSort.add(superClass);
            }
            Class[] interfaces = c.getInterfaces();
            if(interfaces.length > 0) {
                classesToSort.addAll(Arrays.asList(interfaces));
            }
            classesToSort.add(c);
        }
    }

我不确定这是否有效。

【问题讨论】:

  • 我们不会为你做作业。到目前为止,您尝试了什么,问题到底出在哪里?
  • 有趣的问题。
  • 其实这就是问题所在。给定一个类列表并根据上述条件对它们进行排序。我们需要编写一个函数,它接受一个类列表并相应地对其进行排序
  • @Mureinik:我编辑了我的问题,希望这对我有帮助或给我建议可以做什么或正确的方法
  • 您的代码实际上并没有对任何内容进行排序,它只是创建一个本地列表然后返回。也许您想返回classesToSort?下一个问题是该列表是否排序正确;在某些课程上尝试一下,如果您遇到特定问题,请发布有关该问题的问题。

标签: java algorithm class sorting inheritance


【解决方案1】:

如果您在 Java 中听到“排序”,请始终想到“Comparator”。如果您有一个Comparator 能够比较给定类型的两个元素(在您的情况下为Class),您可以使用Collections.sort(elements, comparator) 对这些元素的列表进行排序。

要写一个Comparator,你需要实现它的方法

public int compare(E el1, E el2);

E 是元素的类型,所以在你的情况下

public int compare(Class<?> c1, Class<?> c2);

因为您正在比较 Class 对象。如果 c1

现在您有两个要求可以帮助您实现比较:

  1. A 类是 B 类的子类
  2. 类 A 实现接口 B

这两个都可以通过使用 Java 在Class 中提供的称为isAssignableFrom 的方法来检查。

c1.isAssignableFrom(c2)

true,如果c1“与指定的类参数所代表的类或接口相同,或者是其超类或超接口”(即c2)——所以基本上是c1.isSuperclassOf(c2)。为了您的比较,这意味着,如果它返回 true,则 c1

所以让我们用它来写Comparator

public HierarchyComparator implements Comparator<Class<?>> {
    public int compare(Class<?> c1, Class<?> c2) {
        int result;
        // we need to do this check because isAssignableFrom is true in this case
        // and we would get an order that doesn't exist
        if (c1.equals(c2)) {
            return 0;
        }
        if (c1.isAssignableFrom(c2)) {
            return -1;
        } else if (c2.isAssignableFrom(c1)) {
            return 1;
        }
        // no hierarchy
        return 0;
    }
}

然后,你可以按类排序

public List<Class<?>> sort(Class<?>... classes) {
    List<Class<?>> result = new ArrayList<>(Arrays.asList(classes));
    Collections.sort(result, new HierarchyComparator());
}

【讨论】:

  • 这是错误的。这个Comparator 没有指定类as required 的总顺序,因为它不是反对称的。所以,Collections.sort 不起作用。
  • @yeputons 反对称不是Comparator#compare 的要求之一,请阅读其文档。它需要在这里实现的“sgn(compare(x, y)) == -sgn(compare(y, x))”,以及传递性。如果我遗漏了什么,请提供一个反样。
  • 对不起,我应该使用严格的引用而不是一些隐含的挥手:如果compare(x, y) == 0,那么xy 的行为方式与任意行为相同z (link)。这不是真的:考虑B extends A 和独立的C。然后是compare(B, C) == 0,但是compare(B, A) == 1compare(C, A) == 0
  • 不,这意味着您的回答违反了Comparator 的合同,这可能导致任何任意结果,例如throwing exceptions。例如,B,C,A, 是有效的排序结果:任意两个相邻元素的compare 为零。所以Collection.sort被允许not modify such list at all
  • Here 是使用比较器对Exception 的一堆直接子类以及Exception 本身进行排序的示例。它失败。您对“现实世界的使用”有什么具体限制,或者对任何可以解决问题的“适当的二级排序”有什么建议吗?
【解决方案2】:

希望这会有所帮助。类 PrintClassHierarchy 需要输入要按层次顺序打印的类。

//sample classes for tutorial
class A{

}

class B extends A {

}


class C extends A {

}


class D extends C {

}
class E extends B {

}
    class F extends B {

    }

//print hierarchy
    public class PrintClassHierarchy {
        private static final String PADDING = "        ";
        private static final String PADDING_WITH_COLUMN = "   |    ";
        private static final String PADDING_WITH_ENTRY = "   |--- ";
        private static final String BASE_CLASS = Object.class.getName();

        private final Map<String, List<String>> subClazzEntries = new HashMap<>();

        public static void main(final String[] args) {
            new PrintClassHierarchy(
                A.class,
                B.class,
                C.class,
                D.class,
                E.class,
                F.class
            ).printHierarchy();
        }

        public PrintClassHierarchy(final Class<?>... clazzes) {
            // get all entries of tree
            traverseClasses(clazzes);
        }

        public void printHierarchy() {
            // print collected entries as ASCII tree
            printHierarchy(BASE_CLASS, new Stack<Boolean>());
        }

        private void printHierarchy(final String clazzName, final Stack<Boolean> moreClassesInHierarchy) {
            if (!moreClassesInHierarchy.empty()) {
                for (final Boolean hasColumn : moreClassesInHierarchy.subList(0, moreClassesInHierarchy.size() - 1)) {
                    System.out.print(hasColumn.booleanValue() ? PADDING_WITH_COLUMN : PADDING);
                }
            }

            if (!moreClassesInHierarchy.empty()) {
                System.out.print(PADDING_WITH_ENTRY);
            }

            System.out.println(clazzName);

            if (subClazzEntries.containsKey(clazzName)) {
                final List<String> list = subClazzEntries.get(clazzName);

                for (int i = 0; i < list.size(); i++) {
                    // if there is another class that comes beneath the next class, flag this level
                    moreClassesInHierarchy.push(new Boolean(i < list.size() - 1));

                printHierarchy(list.get(i), moreClassesInHierarchy);

                moreClassesInHierarchy.removeElementAt(moreClassesInHierarchy.size() - 1);
            }
        }
    }

    private void traverseClasses(final Class<?>... clazzes) {
        // do the traverseClasses on each provided class (possible since Java 8)
        Arrays.asList(clazzes).forEach(c -> traverseClasses(c, 0));
    }

    private void traverseClasses(final Class<?> clazz, final int level) {
        final Class<?> superClazz = clazz.getSuperclass();

        if (superClazz == null) {
            // we arrived java.lang.Object
            return;
        }

        final String name = clazz.getName();
        final String superName = superClazz.getName();

        if (subClazzEntries.containsKey(superName)) {
            final List<String> list = subClazzEntries.get(superName);

            if (!list.contains(name)) {
                list.add(name);
                Collections.sort(list); // SortedList
            }
        } else {
            subClazzEntries.put(superName, new ArrayList<String>(Arrays.asList(name)));
        }

        traverseClasses(superClazz, level + 1);
    }
}

输出:

java.lang.Object
       |--- A
               |--- B
               |       |--- E
               |       |--- F
               |--- C
                       |--- D

【讨论】:

    【解决方案3】:

    你可以使用构造函数来做到这一点。

    List<String> listOfClasses = new ArrayList<>();
    public class MyClass{
       public MyClass(){
          listOfClasses.add(this.getClass().getSimpleName());
       }
    }
    public class SubClass extends MyClass{
       public SubClass(){
           // here, first statement super() which calls constructor of superclass
           listOfClasses.add(this.getClass().getSimpleName());
       } 
    }
    

    所以,当你在子类中创建对象时,所有类的构造函数都会被调用,并以排序的方式存储在 listOfClasses 中。

    【讨论】:

    • 如何对类列表进行排序?
    • 使用任何全局变量,如字符串列表并在每个构造函数中调用listOfstring.add(className)。然后 listOfString 按排序顺序包含所有类。
    猜你喜欢
    • 1970-01-01
    • 2016-06-12
    • 2021-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-23
    相关资源
    最近更新 更多