过滤泛型类型列表

发布于 2024-12-01 16:57:49 字数 2793 浏览 0 评论 0 原文

可以使用 guava 过滤器(Iterable未过滤,Class类型)。此操作执行两个任务:列表被过滤转换为给定类型T的序列。

然而,我经常得到Iterables> 我想为一些专门的 T 获取 Iterables> 的子序列。

很明显,由于类型擦除,Guava 无法开箱即用地解决这个问题: < code>Something 不提供有关其 T 的任何直接信息。

假设我有类似 S。 如果我能够定义一些谓词来告诉我 S 是否可以转换为 S 我可以将它用作文件管理器:

<T extends Number> Predicate<S<?>> isOfType(Class<N> type) {...}

with:

Iterable<S<?>> numbers;
Iterable<S<?>> filtered = Iterable.filter(numbers, isOfType(Double.class));

This执行过滤任务,但错过了转换步骤。 如果我认为我的谓词工作得很好,我什至可能会考虑强制转换:

Iterable<S<Double>> doubles = (Iterable<S<Double>>) filtered;

但这暴露了一些丑陋的强制转换操作。

作为替代方案,我可以提供Function, S>来执行转换。 与 Class.cast() 但是,如果元素无法转换(或转化)。 这样,序列就可以在没有任何显式强制转换的情况下进行转换:

<T extends Number> Function<S<?>, S<T>> castOrNull(Class<N> type) {...}

Iterable<S<Double>> doubles = Iterable.filter(numbers, castOrNull(Double.class));

但列表并未真正过滤:相反,它仍然包含无法转换或强制转换为 S 的每个元素的 null 对象。 但这可以通过额外的过滤步骤轻松解决,例如:

Iterable<S<Double>> doubles = Iterables.filter(doubles, Predicates.notNull());

第二个解决方案对我来说似乎更聪明。要定义的Function可以执行强制转换(隐藏未经检查的操作),或者如果需要的话它可以真正创建一些新的对象S

剩下的问题是: 有没有更智能的方法来通过一步执行必要的转换和过滤?我可以简单地定义一些实用函数,例如:

<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>。转换器函数 Function, Iterable> 可以简单地返回过滤后的序列,该序列可能为空,而不是返回 null。附加过滤器最终可能会使用 Iterables.isEmpty() 删除空序列。

Lists or Iterables can be filtered easily using guavas filter(Iterable<?> unfiltered, Class<T> type). This operation performs two tasks: the list is filtered and transformed into a sequence of the given type T.

Quite often however I end up with Iterables<Something<?>>
and I want to get a subsequence of Iterables<Something<T>> for some specialized T.

It is clear, that Guava can't solve this problem out of the box due to type erasure: Something<T> does not provide any direct information about its T.

Lets say I have something like S<? extends Number>.
If I am able to define some predicate which tells me if S<?> may be casted to S<Double> I may use it as a filer:

<T extends Number> Predicate<S<?>> isOfType(Class<N> type) {...}

with:

Iterable<S<?>> numbers;
Iterable<S<?>> filtered = Iterable.filter(numbers, isOfType(Double.class));

This performs the task of filtering but it misses the transformation step.
If I think my Predicate works well I may even think of casting:

Iterable<S<Double>> doubles = (Iterable<S<Double>>) filtered;

But this exposes some ugly cast operation.

As an alternative I may provide a Function<S<?>, S<Double>> to perform the cast.
In constrast to Class.cast() however it should not throw a ClassCastException but simply return null if the element can not be casted (or transformed).
This way the sequence may be converted without any explicit cast:

<T extends Number> Function<S<?>, S<T>> castOrNull(Class<N> type) {...}

Iterable<S<Double>> doubles = Iterable.filter(numbers, castOrNull(Double.class));

But the list is not really filtered: instead it still contains null objects for each element which could not converted or casted to S<Double>.
But this may solved easily by an additional filtering step like:

Iterable<S<Double>> doubles = Iterables.filter(doubles, Predicates.notNull());

The second solution seems much smarter to me. The Function to be defined may either perform a cast (which hides the unchecked operation) or it may really create some new Object S<T> if necessary.

The remaining question is:
Is there any smarter way to perform the necessary converting and filtering by a single step? I may simply define some utility function like:

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

Where the second function is a short cut of the first one with a Predicates.notNull();

But it's worth to have the first function, too, as the predicate is not necessary Predicates.notNull().

Imagine an Iterable<Iterable<? extends Number>>. The converter function Function<Iterable<? extends Number>, Iterable<Double>> may simply return a filtered sequence which may be empty instead of returning null. The additional filter may finally drop empty sequences using Iterables.isEmpty().

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

旧城烟雨 2024-12-08 16:57:49

解决此问题的单子方法是定义一个将可迭代对象转换为可迭代对象的可迭代对象的操作,方法是定义一个转换函数,对于 T 类型的对象,返回一个 Iterable< 类型的对象;T>。然后,您可以将每个可迭代对象连接起来,再次形成一个可迭代对象。这种映射后跟串联的组合在 Haskell 中称为 concatMap ,在 Scala 中称为 flatMap ,我确信它在其他地方还有其他名称。

为了实现这一点,我们首先创建一个函数来转换您的 S 扩展为 Iterable>。这与您现有的函数非常相似,但我们的成功案例是一个可迭代的,包含我们的 S,而失败案例(我们的 null 状态)是一个空的可迭代。

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

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

The monadic approach to this problem is to define an operation that transforms an iterable into an iterable of iterables, by defining a transformation function that for an object of type T, returns an object of type Iterable<T>. You can then concatenate each iterable to form a single one again. This combination of a mapping followed by a concatenation is called concatMap in Haskell and flatMap in Scala, and I'm sure it has other names elsewhere.

To implement this, we first create a function that transforms your S<? extends Number> into Iterable<S<Double>>. This is very similar to your existing function, but our success case is an iterable of one, containing our S, and the failure case (our null state) is an empty iterable.

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

We then apply this to the original iterable as you specify above.

Iterable<Iterable<S<Double>>> doubleIterables = Iterables.map(numbers, castOrNull(Double.class));

We can then concatenate all these together to produce one iterable again, which has all of the desired values and none of those we want to remove.

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

Disclaimer: I haven't tried compiling this. You may have to play around with generics to get it to work.

花期渐远 2024-12-08 16:57:49

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

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

Scala language in its collections framework offers similar functionality to Guava. We have Option[T] class which can be considered as at-most-single-element-collection. Among simple filtering or transformation methods there is a method which performs both operations at once. It expects provided transformation function to return a value of Option class. Then it merges content of returned Option objects into a collection. I think you can implement similar functionality in Java.

I was thinking of this problem some time ago because firstly applying transformation and then filtering requires passing the collection twice. Then someone enlightened me that I can transform and filter iterator of this collection. In this case the collection is traversed once and you may apply as many filters and transformations as you want.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文