筛选泛型类型的列表

2022-09-02 14:05:37

列表或可迭代可以使用番石榴过滤器(可迭代<>未过滤,类<T>类型)轻松过滤。此操作执行两个任务:筛选列表并将其转换为给定类型 T 的序列。

然而,很多时候我最终会得到,我想得到一些专门的T的子序列。Iterables<Something<?>>Iterables<Something<T>>

很明显,由于类型擦除,番石榴无法开箱即用地解决此问题:没有提供有关其T的任何直接信息。Something<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;

但这暴露了一些丑陋的演员操作。

作为替代方案,我可能会提供一个来执行演员表。然而,在constrast到Class.cast()中,它不应该抛出一个,但如果元素不能被强制转换(或转换),它只应该返回。这样,序列可以在没有任何显式转换的情况下进行转换:Function<S<?>, S<Double>>ClassCastExceptionnull

<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());

第二种解决方案对我来说似乎要聪明得多。要定义的可以执行强制转换(隐藏未选中的操作),或者如有必要,它可能会真正创建一些新对象。FunctionS<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()

想象一个.转换器函数可以简单地返回一个过滤的序列,该序列可能是空的,而不是返回null。附加筛选器可能最终使用 删除空序列。Iterable<Iterable<? extends Number>>Function<Iterable<? extends Number>, Iterable<Double>>Iterables.isEmpty()


答案 1

解决这个问题的一元方法是定义一个操作,该操作将可迭代对象转换为可迭代对象的可迭代对象,通过定义一个转换函数,该转换函数对于类型的对象返回一个类型的对象。然后,您可以连接每个可迭代对象以再次形成单个可迭代对象。这种映射后跟串联的组合在Haskell和Scala中被称为,我敢肯定它在其他地方还有其他名称。TIterable<T>concatMapflatMap

为了实现这一点,我们首先创建一个函数,将您转换为 .这与现有函数非常相似,但我们的成功案例是一个可迭代的函数,包含我们的 ,而失败案例(我们的空状态)是一个空的可迭代函数。S<? extends Number>Iterable<S<Double>>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));

然后,我们可以将所有这些连接在一起,再次生成一个可迭代的值,该值具有所有所需的值,并且没有我们想要删除的值。

Iterable<S<Double>> doubles = Iterables.concat(doubleIterables);

免责声明:我还没有尝试编译这个。您可能需要尝试使用泛型才能使其正常工作。


答案 2

Scala语言在其集合框架中提供了与Guava类似的功能。我们有 Option[T] 类,它最多可以被认为是单元素集合。在简单的筛选或转换方法中,有一种方法可以同时执行这两个操作。它期望提供的转换函数返回 Option 类的值。然后,它将返回的 Option 对象的内容合并到一个集合中。我认为你可以在Java中实现类似的功能。

我前段时间就想到了这个问题,因为首先应用转换,然后进行筛选需要传递集合两次。然后有人启发了我,我可以转换和过滤这个集合的迭代器。在这种情况下,集合将遍历一次,您可以根据需要应用任意数量的筛选器和转换。


推荐