简体   繁体   English

过滤泛型类型列表

[英]Filtering lists of generic types

Lists or Iterables can be filtered easily using guavas filter(Iterable<?> unfiltered, Class<T> type) . 可以使用guavas filter(Iterable<?> unfiltered, Class<T> type)轻松过滤列表或Iterables 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. 此操作执行两个任务:列表被过滤转换为给定类型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. 然而,我常常以Iterables<Something<?>> ,我希望获得一些专门的T的Iterables<Something<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. 很明显,由于类型擦除,番石榴无法解决这个问题: Something<T>没有提供有关其T的任何直接信息。

Lets say I have something like S<? extends Number> 可以说我有像S<? extends Number> S<? extends Number> . 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: 如果我能够定义一些谓词告诉我S<?>可以转换为S<Double>我可以将它用作文件管理器:

<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: 如果我认为我的Predicate运作良好,我甚至可以考虑转换:

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. 作为替代方案,我可以提供一个Function<S<?>, S<Double>>来执行演员表。 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). Class.cast()相反,它不应该抛出ClassCastException而只是在元素无法转换(或转换)时返回null 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> . 但是列表并没有真正过滤:相反,它仍然包含每个元素的空对象,这些元素无法转换或转换为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. 要定义的Function可以执行强制转换(隐藏未经检查的操作),也可以在必要时创建一些新的对象S<T>

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() ; 第二个函数是第一个函数的快捷方式,其中包含Predicates.notNull() ;

But it's worth to have the first function, too, as the predicate is not necessary Predicates.notNull() . 但是也值得拥有第一个函数,因为谓词不是必需的Predicates.notNull()

Imagine an Iterable<Iterable<? extends Number>> 想象一下Iterable<Iterable<? extends Number>> Iterable<Iterable<? extends Number>> . Iterable<Iterable<? extends Number>> The converter function Function<Iterable<? extends Number>, Iterable<Double>> 转换器功能Function<Iterable<? extends Number>, Iterable<Double>> Function<Iterable<? extends Number>, Iterable<Double>> may simply return a filtered sequence which may be empty instead of returning null. Function<Iterable<? extends Number>, Iterable<Double>>可能只返回一个过滤的序列,该序列可能为空而不是返回null。 The additional filter may finally drop empty sequences using Iterables.isEmpty() . 附加过滤器最终可能会使用Iterables.isEmpty()删除空序列。

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> . 解决此问题的monadic方法是定义一个操作,通过为类型为T的对象定义转换函数,将迭代转换为可迭代的迭代,返回Iterable<T>类型的对象。 You can then concatenate each iterable to form a single one again. 然后,您可以连接每个iterable以再次形成一个。 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. 映射后跟连接的concatMap在Haskell中称为concatMap ,在Scala中称为flatMap ,我确信它在其他地方有其他名称。

To implement this, we first create a function that transforms your S<? extends Number> 为了实现这一点,我们首先创建一个转换S<? extends Number>的函数S<? extends Number> S<? extends Number> into Iterable<S<Double>> . S<? extends Number>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. 这与您现有的函数非常相似,但我们的成功案例是一个可迭代的,包含我们的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));
        }
    };
}

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. 您可能必须使用泛型来使其工作。

Scala language in its collections framework offers similar functionality to Guava. 其集合框架中的Scala语言提供与Guava类似的功能。 We have Option[T] class which can be considered as at-most-single-element-collection. 我们有Option [T]类,可以认为是最多单元素集合。 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. 它期望提供的转换函数返回Option类的值。 Then it merges content of returned Option objects into a collection. 然后它将返回的Option对象的内容合并到一个集合中。 I think you can implement similar functionality in Java. 我认为您可以在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. 在这种情况下,集合将遍历一次,您可以根据需要应用尽可能多的过滤器和转换。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM