过滤泛型类型列表

时间:2011-08-26 12:29:20

标签: java generics guava iterable

可以使用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()删除空序列。

2 个答案:

答案 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中实现类似的功能。

前段时间我正在考虑这个问题,因为首先应用转换然后过滤需要将集合传递两次。然后有人启发我,我可以转换和过滤此集合的迭代器。在这种情况下,集合将遍历一次,您可以根据需要应用尽可能多的过滤器和转换。