我有一个功能:
void foo(Collection<? extends Baz> messages) {
如何确定集合中所有元素的派生最多的通用子类?
可能所有元素都扩展了Bar
,后者也扩展了Baz
。有些可以扩展Fuz
,而扩展Bar
,但不是全部;在这种情况下,最派生的通用子类仍然是Bar
。
答案 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;
}
这是最常见的情况,因此它也适用于您的Baz
和Bar
。
答案 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条