问题描述
我正在慢慢学习新的Java 8功能,我正在尝试找到一种方法来处理类层次结构(从子级到父级)作为流。
I am slowly learning the new Java 8 features and I am trying to find a way to process a class hierarchy (from child to parent) as a stream.
例如,在类或它的父类上查找注释。
For instance find an annotation on a class or it's parents.
在Java 8之前,我会这样做:
Before Java 8, I would have done it this way :
public static <T extends Annotation> T getAnnonationOn(Class<?> type, Class<T> annType) {
Class<?> t = type;
T annot = null;
while (t != null && annot == null) {
annot = t.getAnnotation(annType);
t = t.getSuperclass();
}
return annot;
}
现在我希望用更多函数式编程方式来实现。
我找不到更好的方法,比使用递归的连接流更好:
Now I wish to do it with a more "functional programming" way.I could not find a better way than concatenate streams with a recursive like follow :
import java.lang.annotation.Annotation;
import java.util.stream.Stream;
public static <T extends Annotation> T getAnnonationOn(Class<?> type, Class<T> annType) {
return ClassIterator.streamSuperclass(type)
.map(t -> t.getAnnotation(annType))
.filter(a -> a != null)
.findFirst()
.orElse(null);
}
public static class ClassIterator {
public static Stream<Class<?>> streamSuperclass(Class<?> type) {
if (type.getSuperclass() != null) {
return Stream.concat(Stream.of(type), Stream.of(type.getSuperclass()).flatMap(ClassIterator::streamSuperclass));
}
return Stream.of(type);
}
}
但我对解决方案不太满意。虽然我没有对它进行基准测试,但我认为流连接非常麻烦并且性能不佳。
But I am not quite satisfied of the solution. Although I did not benchmark it I think the stream concatenation is quite cumbersome and under performant.
有没有更好的方法将递归转换为流?
Is there a better way to turn a recursive into a stream ?
推荐答案
在Java 9中,您可能会使用
In Java 9, you would probably use
public static Stream<Class<?>> streamSuperclass(Class<?> type) {
return Stream.iterate(type, Objects::nonNull, Class::getSuperclass);
}
但在Java 8中,此功能不可用,因此您可以使用手动实现Stream:
but in Java 8, this feature is not available, so you may resort to implement the Stream manually:
public static Stream<Class<?>> streamSuperclass(Class<?> type) {
return StreamSupport.stream(
new Spliterators.AbstractSpliterator<Class<?>>(100L,
Spliterator.ORDERED|Spliterator.IMMUTABLE|Spliterator.NONNULL) {
Class<?> current = type;
public boolean tryAdvance(Consumer<? super Class<?>> action) {
if(current == null) return false;
action.accept(current);
current = current.getSuperclass();
return true;
}
}, false);
}
请注意,这将从最具体的类型流向 java.lang.Object中
。如果您希望订单从 Object
到最具体的订单,则无法首先收集元素,无论是递归还是迭代,都不是那么重要,但 Stream.concat
确实是性能最差的变体。您只需使用
Note that this will stream from the most specific type towards java.lang.Object
. If you want the order to be from Object
to the most specific one, there is no way around gathering the elements first, whether recursive or iterative, is not so important, but Stream.concat
is indeed the least performance variant. You can simply use
public static Stream<Class<?>> streamSuperclass(Class<?> type) {
return reverse(Stream.<Class<?>>builder(), type, Class::getSuperclass).build();
}
private static <T> Stream.Builder<T> reverse(
Stream.Builder<T> builder, T t, UnaryOperator<T> op) {
return t==null? builder: reverse(builder, op.apply(t), op).add(t);
}
迭代变体也不是那么糟糕:
The iterative variant also is not so bad:
public static Stream<Class<?>> streamSuperclass(Class<?> type) {
List<Class<?>> l=new ArrayList<>();
for(; type!=null; type=type.getSuperclass()) l.add(type);
Collections.reverse(l);
return l.stream();
}
对于像典型类层次结构一样小的流, ArrayList
并不比 Stream.Builder
更糟糕,对于非常大的流,使用递归填充构建器可能不是最好的解决方案......
For streams as small as a typical class hierarchy, an ArrayList
is not worse than a Stream.Builder
and for very large streams, filling the builder using a recursion might not be the best solution either…
这篇关于Java8 Streaming类层次结构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!