Поставщики коллекций Google и найти
Вопрос
Я ищу метод коллекций Google, который возвращает первый результат последовательности поставщиков, которые не возвращают нуль.
Я смотрел на использование iterables.find (), но в своем предикате мне пришлось бы позвонить своему поставщику, чтобы сравнить результат с NULL, а затем пришлось бы вызвать его снова, как только метод поиска вернет поставщика.
Решение
Учитывая ваш комментарий, чтобы успокоить ответ Шторма (желание не звонить 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());
Обратите внимание, что итерабильный, который выходит из Iterables.transform()
лениво оценивается, поэтому как Iterables.find()
петли над этим, вы оцениваете только первую неnull
-Пересмотр один, и это только один раз.
Другие советы
Вы спросили, как это сделать, используя коллекции Google, но вот как бы вы сделали это без использования коллекций Google. Сравните это с ответом Коуэна (который является хорошим ответом) - что легче понять?
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
}
Вместо комментариев - если это была вина вызывающего абонента вашего класса, добавьте allectalargumentException или allectuststateexception в зависимости от времени; Если этого никогда не произошло, используйте AssertionError; Если это нормальное событие, который вызывает ваш код, который должен быть проверен, вы можете вернуть 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;
}
});
Вы пытаетесь сохранить несколько строк? Единственная оптимизация, которую я могу подумать, - это статический импорт находки, чтобы вы могли избавиться от «иеры». Кроме того, предикатом является анонимный внутренний класс, если он вам нужен в нескольких местах, вы можете создать класс, и он будет выглядеть как,
List<Supplier> supplierList = //somehow get the list
Supplier s = find(supplierList, new SupplierPredicateFinder());
Где поставленное производство - еще один класс.
ОБНОВЛЕНИЕ: В этом случае найти неправильный метод. Вам действительно нужна такая пользовательская функция, которая может вернуть два значения. Если вы используете Commons-Collense, вы можете использовать по умолчанию Mapentry или просто вернуть объект [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 :)