我正在寻找一种 Google Collections 方法,该方法返回不返回 null 的供应商序列的第一个结果。
我正在考虑使用 Iterables.find() 但在我的谓词中,我必须调用我的供应商以将结果与 null 进行比较,然后在 find 方法返回供应商后再次调用它。
鉴于您对平静风暴的回答的评论(不想打Supplier.get()
两次电话),那么:
private static final Function<Supplier<X>, X> SUPPLY = new Function<....>() {
public X apply(Supplier<X> in) {
// If you will never have a null Supplier, you can skip the test;
// otherwise, null Supplier will be treated same as one that returns null
// from get(), i.e. skipped
return (in == null) ? null : in.get();
}
}
然后
Iterable<Supplier<X>> suppliers = ... wherever this comes from ...
Iterable<X> supplied = Iterables.transform(suppliers, SUPPLY);
X first = Iterables.find(supplied, Predicates.notNull());
请注意,产生的 IterableIterables.transform()
是惰性求值的,因此在对其进行Iterables.find()
循环时,您只求值到第一个非null
返回值,并且只求值一次。
您询问了如何使用 Google 收藏集来执行此操作,但以下是您在不使用 Google 收藏集的情况下如何执行此操作。将其与 Cowan 的答案(这是一个很好的答案)进行比较——哪个更容易理解?
private static Thing findThing(List<Supplier<Thing>> thingSuppliers) {
for (Supplier<Thing> supplier : thingSuppliers) {
Thing thing = supplier.get();
if (thing != null) {
return thing;
}
}
// throw exception or return null
}
代替注释——如果这是你的类的调用者的错,适当地抛出 IllegalArgumentException 或 IllegalStateException;如果这不应该发生,请使用 AssertionError;如果调用 this 的代码需要检查是正常情况,则可能返回 null。
这有什么问题?
List<Supplier> supplierList = //somehow get the list
Supplier s = Iterables.find(supplierList, new Predicate<Supplier>(){
boolean apply(Supplier supplier) {
return supplier.isSomeMethodCall() == null;
}
boolean equals(Object o) {
return false;
}
});
你想保存一些行吗?我能想到的唯一优化是静态导入查找,这样你就可以摆脱“Iterables”。谓词也是一个匿名内部类,如果你在多个地方需要它,你可以创建一个类,它看起来像,
List<Supplier> supplierList = //somehow get the list
Supplier s = find(supplierList, new SupplierPredicateFinder());
SupplierPredicateFinder 是另一个类。
更新:在这种情况下 find 是错误的方法。你实际上需要一个这样的自定义函数,它可以返回两个值。如果您使用的是 commons-collections,那么您可以使用 DefaultMapEntry,或者您可以简单地返回 Object[2] 或 Map.Entry。
public static DefaultMapEntry getSupplier(List<Supplier> list) {
for(Supplier s : list) {
Object heavyObject = s.invokeCostlyMethod();
if(heavyObject != null) {
return new DefaultMapEntry(s, heavyObject);
}
}
}
用大小为 2 的列表或大小为 1 的哈希图或长度为 2 的数组替换 DefaultMapEntry :)