查找集合中所有对象的通用类

时间:2019-06-17 12:36:39

标签: java

我有一个功能:

void foo(Collection<? extends Baz> messages) {

如何确定集合中所有元素的派生最多的通用子类?

可能所有元素都扩展了Bar,后者也扩展了Baz。有些可以扩展Fuz,而扩展Bar,但不是全部;在这种情况下,最派生的通用子类仍然是Bar

9 个答案:

答案 0 :(得分:2)

迭代集合并将每个元素转换为其层次结构中的类列表(例如Fuz-Bar-Baz-Object)。

对于随后的每个元素,classList.retainAll()都是相同操作的结果。

取结果列表的第一个元素。

编辑:

自从您将其标记为回答您的问题以来,我实际上将进行更多工作。

这是我的写法:

public static Class mostDerived(Collection<?> objects) {
    Optional<List<Class<?>>> mostDerived =
        objects.stream()
               .map(Util::getHierarchy) // class list for each
               .reduce((l1, l2) -> {
                   l1.retainAll(l2); // get intersecting classes
                   return l1;
               });
    return mostDerived.map(l -> l.get(0)).orElse(null);
}

private static List<Class<?>> getHierarchy(Object l) {
    List<Class<?>> result = new ArrayList<>();
    for (Class<?> clz = l.getClass(); clz != null; clz = clz.getSuperclass()) {
        result.add(clz);
    }
    return result;
}

Here's it running和测试用例。

答案 1 :(得分:0)

如果所有邮件均为特定类型,您似乎希望能够检查某些特定情况。

如果是这样,那就去:

if(messages.stream().allMatch(i -> i instanceof SomeObject)) {
     ...
}

否则,您也许可以使用反射API编写嵌套搜索...但是我认为那很快就会变得非常丑陋。

答案 2 :(得分:0)

这是我已经想出的一种方法,可以为您返回对象集合中最接近的祖先:

static Class<?> getCommonAncestor(Collection<?> messages) {
    return messages.stream().map(Object::getClass)
        .collect(Collectors.reducing(Main::getCommonAncestor))
        .orElseThrow(() -> new RuntimeException("The collection is empty!"));
}

// this method gets the closest common ancestor between 2 classes
static Class<?> getCommonAncestor(Class<?> a, Class<?> b) {
    Stack<Class<?>> aParents = getParents(a);
    Stack<Class<?>> bParents = getParents(b);

    Class<?> commonAncestor = null;

    // I decided to search from the top of the inheritance tree because i know that
    // there will be the same number of classes on top of a common ancestor
    // I feel like there is a faster way, but I can't think of it...
    while (!aParents.isEmpty() && !bParents.isEmpty()) {
        Class<?> aPopped = aParents.pop();
        Class<?> bPopped = bParents.pop();
        if (aPopped == bPopped) {
            commonAncestor = aPopped;
        }
    }
    return commonAncestor;

}

// this method gets the whole inheritance tree of a single class
static Stack<Class<?>> getParents(Class<?> c) {
    Class<?> clazz = c;
    Stack<Class<?>> parents = new Stack<>();
    parents.push(clazz);
    while (parents.peek() != Object.class) {
        parents.push(clazz.getSuperclass());
        clazz = clazz.getSuperclass();
    }
    return parents;
}

这是最常见的情况,因此它也适用于您的BazBar

答案 3 :(得分:0)

给出一个子类列表predefinedClassList,该列表以升序排序(从最小到大多数子类),可在运行时使用:

for (Baz b : messages) {

   predefinedClassList.stream()
      .filter(c -> messages.stream()
         .allMatch(m -> m.getClass().instanceOf(c)))
      .reduce((a, b) -> b).orElse(Baz.class));
   }
}

答案 4 :(得分:0)

我将使用getClass()并将给定Class的实例数存储在地图中:

void foo(Collection<? extends Baz> messages) {
    IdentityHashMap<Class, Integer> allCounts = new IdentityHashMap<>();
    for( Baz message : messages )
    {
        int count = allCounts.getOrDefault(message.getClass(), 0);
        allCounts.put((message.getClass(), count++);
    }
    // Query the map as needed.
}

答案 5 :(得分:0)

public static <T> Class<? extends T> getMostCommonParent(Collection<T> messages) {
    Map<Class<?>, Integer> map = new HashMap<>();

    for (T message : messages) {
        Class<?> cls = message.getClass();

        while (cls != Object.class) {
            map.compute(cls, (key, total) -> Optional.ofNullable(total).orElse(0) + 1);
            cls = cls.getSuperclass();
        }
    }

    Class<?> cls = messages.iterator().next().getClass();

    while(map.get(cls) != messages.size())
        cls = cls.getSuperclass();

    return (Class<? extends T>)cls;
}

答案 6 :(得分:0)

您可以遍历每个对象,并获取所有类。

List<Class<?>> getClasses(Object o){
    List<Class<?>> classes = new HashSet<>();
    Class<?> c = o.getClass();
    while(c!=null){
        classes.add(c);
        c = c.getSuperClass();
    }
    return classes;
}

创建列表集合。

List<List<Class<?>> allClasses = messages.stream().map(
                                    this::getClasses
                                 ).collect(
                                    Collectors.toList()
                                 );
Set<Class<?>> union = new HashSet<>(allClasses.get(0));
allClasses.forEach(union::retainAll);

此时,union具有所有常见的类。要找到最指定的一个...我将在allClasses中的第一个列表上循环,然后找到第一个元素。

Optional<Class<?>> mostSpecified = allClasses.get(0).filter(
                                        union::contains
                                   ).findFirst();

答案 7 :(得分:0)

您可以找到所有常见的超类,然后将其放在层次结构的底部。但是,这并未考虑接口:

public static void main(final String[] args) {
    final Collection<Object> messages = Arrays.asList(
            new Bar(), new Foo());

    final Set<Class<?>> commonSuperclasses = messages.stream()
            .map(c -> c.getClass())
            .map(c -> getSuperclasses(new HashSet<>(), c))
            .reduce(Sets::intersection) // guava
            .orElse(Collections.emptySet());

    final Class<?> mostSpecificCommonSuperclass =
            commonSuperclasses
                    .stream()
                    .filter(
                            // take the one at the bottom of the hierarchy
                            c -> commonSuperclasses
                                    .stream()
                                    .map(s -> s.getSuperclass())
                                    .noneMatch(c::equals))
                    .findFirst()
                    .orElse(null);

    System.out.println(mostSpecificCommonSuperclass); // prints Bar
}

static Set<Class<?>> getSuperclasses(final Set<Class<?>> acc, final Class<?> clazz) {
    if (clazz == null) {
        return acc;
    }

    return getSuperclasses(Sets.union(acc, Collections.singleton(clazz)), clazz.getSuperclass());
}

答案 8 :(得分:0)

有一种方法可以使用纯Stream递归浏览类直到根Baz类,并且使用groupingBy我们可以获得Class统计信息< strong>分布,例如:

public static Map<Class, Long> foo(Collection<? extends Baz> messages) {
    return messages.stream()
        .map(Object::getClass)
        .distinct()
        .flatMap(i -> getParentClazzs(i, Baz.class))
        .filter(i -> !Objects.equals(i, Baz.class))
        .collect(
            Collectors.groupingBy(Function.identity(), Collectors.counting())
        );
}

private static Stream<Class> getParentClazzs(Class clazz, Class<Baz> rootClass) {
    if (Objects.equals(clazz, rootClass)) {
        return Stream.of(clazz);
    }

    return Stream.concat(Stream.of(clazz), getParentClazzs(clazz.getSuperclass(), rootClass));
}
foo(Arrays.asList(new Bar(), new Foo(), new Bar(), new C(), new D()))
        .entrySet().stream()
        .forEach(i -> System.out.println(i.getKey().getSimpleName() + " " + i.getValue()));

输出: C->酒吧->巴兹

D->酒吧->巴兹

Foo-> Baz

  

C 1

     

D 1

     

Foo 1

     

第3条