可以使用guavas filter(Iterable<?> unfiltered, Class<T> type)
轻松过滤列表或Iterables。此操作执行两个任务:将列表过滤并转换为给定类型T的序列。
但我常常以Iterables<Something<?>>
结束
我希望得到一些专门的T. {/ 1>的Iterables<Something<T>>
的子序列
很明显,由于类型擦除,Guava无法解决这个问题:Something<T>
没有提供有关其T的任何直接信息。
让我说我有S<? extends Number>
之类的东西。
如果我能够定义一些谓词,告诉我S<?>
是否可以转换为S<Double>
我可以将其用作文件管理器:
<T extends Number> Predicate<S<?>> isOfType(Class<N> type) {...}
使用:
Iterable<S<?>> numbers;
Iterable<S<?>> filtered = Iterable.filter(numbers, isOfType(Double.class));
这执行过滤任务,但它错过了转换步骤。 如果我认为我的谓词运作良好,我甚至可以考虑投射:
Iterable<S<Double>> doubles = (Iterable<S<Double>>) filtered;
但是这暴露了一些丑陋的施法操作。
作为替代方案,我可以提供Function<S<?>, S<Double>>
来执行演员表。
然而,与Class.cast()
相比,它不应该抛出ClassCastException
,而只是在元素无法转换(或转换)时返回null
。
这样,序列可以在没有任何显式转换的情况下进行转换:
<T extends Number> Function<S<?>, S<T>> castOrNull(Class<N> type) {...}
Iterable<S<Double>> doubles = Iterable.filter(numbers, castOrNull(Double.class));
但是列表并没有真正过滤:相反,它仍然包含无法转换或转换为S<Double>
的每个元素的空对象。
但这可以通过一个额外的过滤步骤轻松解决,如:
Iterable<S<Double>> doubles = Iterables.filter(doubles, Predicates.notNull());
第二种解决方案对我来说似乎更聪明。要定义的Function
可以执行强制转换(隐藏未经检查的操作),也可以在必要时创建一些新的对象S<T>
。
剩下的问题是: 是否有更聪明的方法可以通过一步执行必要的转换和过滤?我可以简单地定义一些效用函数,如:
<I,O> Iterables<O> convert(
Iterables<O> input,
Function<? super I, ? extends O> convert,
Predicate<? super O> filter);
<I,O> Iterables<O> convert(
Iterables<O> input,
Function<? super I, ? extends O> convert);
第二个函数是第一个函数的捷径,其中Predicates.notNull()
;
但是,第一个函数也是值得的,因为谓词不是必需的Predicates.notNull()
。
想象一下Iterable<Iterable<? extends Number>>
。转换器函数Function<Iterable<? extends Number>, Iterable<Double>>
可以简单地返回过滤的序列,该序列可以是空的而不是返回null。附加过滤器最终可能会使用Iterables.isEmpty()
删除空序列。
答案 0 :(得分:3)
解决此问题的monadic方法是定义一个操作,通过定义一个类型为T
的对象的转换函数,将迭代转换为可迭代的迭代,返回类型为{{1}的对象}。然后,您可以连接每个iterable以再次形成一个。映射后跟连接的组合在Haskell中称为Iterable<T>
,在Scala中称为concatMap
,我确信它在别处有其他名称。
为了实现这一点,我们首先创建一个将flatMap
转换为S<? extends Number>
的函数。这与您现有的函数非常相似,但我们的成功案例是一个可迭代的,包含我们的Iterable<S<Double>>
,而失败案例(我们的null状态)是一个空的可迭代。
S
然后我们将其应用于您在上面指定的原始迭代。
<T extends Number> Function<S<?>, Iterable<S<T>>> castOrNull(Class<T> type) {
return new Function<S<?>, Iterable<S<T>>> {
@Override
public Iterable<S<T>> apply(S<?> s) {
Object contained = s.get();
if (!(contained instanceof T)) {
return ImmutableSet.of();
}
return ImmutableSet.of(new S<T>(contained));
}
};
}
然后我们可以将所有这些连接起来再次生成一个可迭代的,它具有所有所需的值,而不是我们想要删除的值。
Iterable<Iterable<S<Double>>> doubleIterables = Iterables.map(numbers, castOrNull(Double.class));
免责声明:我没有尝试过编译。您可能需要使用泛型来使其工作。
答案 1 :(得分:2)
其集合框架中的Scala语言提供与Guava类似的功能。我们有Option [T]类,可以认为是最多单元素集合。在简单的过滤或变换方法中,存在一次执行两个操作的方法。它期望提供的转换函数返回Option类的值。然后它将返回的Option对象的内容合并到一个集合中。我认为您可以在Java中实现类似的功能。
前段时间我正在考虑这个问题,因为首先应用转换然后过滤需要将集合传递两次。然后有人启发我,我可以转换和过滤此集合的迭代器。在这种情况下,集合将遍历一次,您可以根据需要应用尽可能多的过滤器和转换。