2011-08-26 55 views
19

使用海藻石filter(Iterable<?> unfiltered, Class<T> type)可以很容易地过滤清单或Iterables。此操作执行两个任务:列表过滤转化为给定类型T的序列过滤一般类型列表

常常不过我最终Iterables<Something<?>> 和我想要得到的Iterables<Something<T>>子序列为一些专门的T.

很清楚,那番石榴不能开箱即用的解决这个问题,因为类型擦除: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()删除空序列。

+1

如果'Iterable.filter(...)'返回一个具有扩展功能的迭代器,那么你可以链式过滤器会很有用。 '/ * S extends Collection */Iterable > doubles = Iterable.filter(numbers,castOrNull(Double.class))。filter(Predicates.notNull())。filter(Predicates.notEmpty());' – aalku

+4

想要一步到位呢?转换和过滤是不同的操作。 – pawstrong

回答

2

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

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

3

的一元的方法解决这个问题是,通过定义一个变换函数,对于T类型的对象,返回Iterable<T>类型的对象来定义,它可将进入可迭代的iterables可迭代的动作。然后,您可以连接每个迭代以再次形成单个迭代。这种映射后接拼接的组合在Haskell中称为concatMap,在Scala中称为flatMap,我确定它在别处有其他名称。

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

声明:我还没有试过编译这个。你可能不得不用泛型来玩它。

+1

让函数返回一个包含零个或一个元素的Iterable是一个非常有趣的方法。我没有想到这一点,我认为它是让你的函数在需要时返回null的好方法。由于Guava在r10中添加了可选类型(http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/base/Optional.html),我想知道我们是否可以使用函数,可选 >>相反,将结果Iterable过滤为“present”值。 –

+1

我刚刚阅读了luckyjaca的回答,看起来这就是他们在Scala中使用的方法,使用“Option”类型以及flatMap函数的附加优势。看到这样的答案更多信息:http://stackoverflow.com/questions/1059776/scala-iterablemap-vs-iterableflatmap/1060400#1060400“'flatMap'变成一个List [Option [A]]'List [ A]',用任何'选项'钻入'None',移除。很酷。 –

+2

@eneveu:如果'Optional'实现'Iterable',那将会很棒,但据我所知,目前还没有计划实现它。然而,Nat Pryce在Java中使用Maybe(https://github.com/npryce/maybe-java)。在我工作的公司工作的一个人分叉并改进了它 - 你可以在https://github.com/youdevise/maybe-java看到这个版本。 –