2009-11-26 48 views
59

使用Google Collections時,我有一個關於簡化某些收集處理代碼的問題(更新Guava)。有沒有一種優雅的方式可以在使用番石榴轉換藏品時刪除空值?

我有一堆「計算機」對象,我想結束他們的「資源ID」的集合。這是像這樣做:

Collection<Computer> matchingComputers = findComputers(); 
Collection<String> resourceIds = 
    Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>() { 
    public String apply(Computer from) { 
     return from.getResourceId(); 
    } 
})); 

現在,getResourceId()可能返回null(和不斷變化的,是不是現在的選項),但在這種情況下,我想從所得到的字符串集合忽略空值。

這裏的過濾空出一個辦法:

Collections2.filter(resourceIds, new Predicate<String>() { 
    @Override 
    public boolean apply(String input) { 
     return input != null; 
    } 
}); 

你可以把所有的一起這樣的:

Collection<String> resourceIds = Collections2.filter(
Lists.newArrayList(Iterables.transform(matchingComputers, new Function<Computer, String>() { 
    public String apply(Computer from) { 
     return from.getResourceId(); 
    } 
})), new Predicate<String>() { 
    @Override 
    public boolean apply(String input) { 
     return input != null; 
    } 
}); 

但是這是很難優雅,更不用說可讀,對於這樣一個簡單的任務!事實上,傳統的Java代碼(沒有花哨的謂語或功能的東西全部)將可以說是更清潔:

Collection<String> resourceIds = Lists.newArrayList(); 
for (Computer computer : matchingComputers) { 
    String resourceId = computer.getResourceId(); 
    if (resourceId != null) { 
     resourceIds.add(resourceId); 
    } 
} 

使用上面的肯定也是一種選擇,但出於好奇(和了解更多的慾望Google收藏集),您是否可以使用Google Collections以更簡短或更優雅的方式完成同樣的事情?

回答

76

有已經Predicates斷言,這將幫助你在這裏 - Predicates.notNull() - 你可以使用Iterables.filter()和事實Lists.newArrayList()可以採取Iterable到清潔這多一點。

Collection<String> resourceIds = Lists.newArrayList(
    Iterables.filter(
    Iterables.transform(matchingComputers, yourFunction), 
    Predicates.notNull() 
) 
); 

如果你實際上並不需要一Collection,只是一個Iterable,那麼Lists.newArrayList()呼叫可以走也和你一步清潔了!

我懷疑你可能會發現,Function會再次派上用場,並且將最有用的聲明爲

public class Computer { 
    // ... 
    public static Function<Computer, String> TO_ID = ...; 
} 

其清除這件事更(並將促進再利用)。

+1

不錯 - 爲什麼我從來沒有發現Predicates方法... –

+0

優秀的建議,謝謝!使用Predicates.notNull()並將函數放入常量中的確可以很好地闡明代碼。 – Jonik

+3

太棒了:)。當我使用函數作爲轉換時,我喜歡用靜態方法將它分離並將其命名爲XXX(),我發現它很容易閱讀。在這種情況下,它可能是:transform(matchingCompters,intoResourceId())。 –

5

首先,我想某處創建一個常數濾波器:

public static final Predicate<Object> NULL_FILTER = new Predicate<Object>() { 
    @Override 
    public boolean apply(Object input) { 
      return input != null; 
    } 
} 

然後你可以使用:

Iterable<String> ids = Iterables.transform(matchingComputers, 
    new Function<Computer, String>() { 
     public String apply(Computer from) { 
      return from.getResourceId(); 
     } 
    })); 
Collection<String> resourceIds = Lists.newArrayList(
    Iterables.filter(ids, NULL_FILTER)); 

你可以在你的代碼在任何地方使用相同的空濾。

如果使用相同的計算功能在其他地方,你可以使一個常數過,只留下:

Collection<String> resourceIds = Lists.newArrayList(
    Iterables.filter(
     Iterables.transform(matchingComputers, RESOURCE_ID_PROJECTION), 
     NULL_FILTER)); 

這當然不是像你一樣的C#相當於會,但是這一切都將得到一個很多在Java 7中更好的與封閉和擴展方法:)

+5

我個人稱之爲NOT_NULL_FILTER。 :)在Predicates類中已經有了一個靜態方法(參見我的答案)。 – Cowan

+1

@Cowan:這取決於你如何對待「過濾器」 - 你可能會爭論它過濾出來的空值。這是命名方面的一般痛苦。不過,我認爲這很明顯,它會做什麼,因爲反過來會很愚蠢:)雖然對Predicates方法有很好的調用。 –

1

你可以像這樣編寫自己的方法。這會爲任何從apply方法返回null的Function過濾出null。

public static <F, T> Collection<T> transformAndFilterNulls(List<F> fromList, Function<? super F, ? extends T> function) { 
     return Collections2.filter(Lists.transform(fromList, function), Predicates.<T>notNull()); 
    } 

然後可以用下面的代碼調用該方法。

Collection c = transformAndFilterNulls(Lists.newArrayList("", "SD", "DDF"), new Function<String, Long>() { 

    @Override 
    public Long apply(String s) { 
     return s.isEmpty() ? 20L : null; 
    } 
}); 
System.err.println(c); 
33

有點 「漂亮」 的語法與FluentIterable(因爲番石榴12):

ImmutableList<String> resourceIds = FluentIterable.from(matchingComputers) 
    .transform(getResourceId) 
    .filter(Predicates.notNull()) 
    .toList(); 

static final Function<Computer, String> getResourceId = 
    new Function<Computer, String>() { 
     @Override 
     public String apply(Computer computer) { 
      return computer.getResourceId(); 
     } 
    }; 

注意返回列表是一個ImmutableList。但是,您可以使用copyInto()方法將元素倒入任意集合中。

13

它花費的時間超過@Jon Skeet expected,但Java的8流做使這個簡單的:

List<String> resourceIds = computers.stream() 
    .map(Computer::getResourceId) 
    .filter(Objects::nonNull) 
    .collect(Collectors.toList()); 

您也可以使用.filter(x -> x != null)如果你喜歡; the difference is very minor

+0

我正在尋找番石榴的'Predicates#notNull()'的Java8等價物。從未想過看'對象' –

+0

以下是API文檔:https://docs.oracle.com/javase/8/docs/api/java/util/Objects.html#nonNull-java.lang.Object- – amoebe