2011-08-26 56 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看到這個版本。 –