Я изучаю Java 8 и хочу переписать обычный Java-код с помощью java Stream. Но сделать это лучше. Мой код:
public Set<Product> getProductsByFilter(Map<String, List<String>>filterParams) {
Set<Product> productsByBrand = new HashSet<Product>();
Set<Product> productsByCategory = new HashSet<Product>();
Set<String> criterias = filterParams.keySet();
if (criterias.contains("brand")) {
for (String brandName : filterParams.get("brand")) {
for (Product product : listOfProducts) {
if (brandName.equalsIgnoreCase(product.
getManufacturer())) {
productsByBrand.add(product);
}
}
}
}
if (criterias.contains("category")) {
for (String category : filterParams.get("category")) {
productsByCategory.addAll(this.
getProductsByCategory(category));
}
}
productsByCategory.retainAll(productsByBrand);
return productsByCategory;
}
Я не знаю, как исправить код переформатирования в if (criterias.contains ("brand")).
Кроме того, похоже, что если какой-либо из двух критериев «бренд» или «категории» не указан, тогда метод должен возвращать пустой набор .... для него более интуитивно понятно предположить, что в случае упущения либо все бренды, либо все категории приемлемы, поэтому в результате получается еще больший набор. Вы можете уточнить?
У меня много брендов и много категорий. У каждого продукта есть одна переменная ссылка на бренд (google, dell, ...) и категории (ноутбук, планшет, смартфон, ...). Этот простой пример для изучения Java 8, Spring MVC, ....
спасибо за разъяснения ... как насчет того факта, что ваш код вернет пустую коллекцию продуктов, если какой-либо критерий фильтра отсутствует во входных данных?
Ваш код выглядит так странно. Если критерий brandсуществуют и критерий categoryне существует, тогда вернуть empty. Почему игнорируется коллекция productsByBrand? Другой момент: если у вас есть доступ к listOfProducts в цикле фильтров бренда, почему бы не использовать этот список в цикле фильтров category? Вы можете просто зациклить один раз
list Of Products - это тестовый список. Данные будут получены из БД.
Что ты скажешь о метоне
общедоступный список <продукт> getProductsByCategory (строковая категория) {return listOfProducts.stream (). filter (p -> p.getCategory (). equalsIgnoreCase (категория)). collect (Collectors.toList ()); }
Если критерий brandсуществуют и критерий categoryне существует, тогда возврат пуст. Почему игнорируется коллекция productsByBrand?




Если код работает, зачем его менять? Тем не менее, вот решение, использующее потоковый API:
public Set<Product> getProductsByFilter(Map<String, List<String>> filterParams) {
Set<Product> productsByBrand = filterParams.containsKey("brand") ?
filterParams.get("brand")
.stream()
.flatMap(brandName -> listOfProducts.stream()
.filter(product -> brandName.equalsIgnoreCase(product.getManufacturer())))
.collect(Collectors.toCollection(HashSet::new)) : new HashSet<>();
Set<Product> productsByCategory =
filterParams.containsKey("category") ?
filterParams.get("category")
.stream()
.flatMap(category -> this.getProductsByCategory(category).stream())
.collect(Collectors.toCollection(HashSet::new)) : new HashSet<>();
productsByCategory.retainAll(productsByBrand);
return productsByCategory;
}
или, как предлагает @shmosel, вы можете избежать тернарного оператора с помощью getOrDefault:
Set<Product> productsByBrand =
filterParams.getOrDefault("brand", Collections.emptyList())
.stream()
.flatMap(brandName -> listOfProducts.stream()
.filter(product -> brandName.equalsIgnoreCase(product.getManufacturer())))
.collect(Collectors.toCollection(HashSet::new));
Set<Product> productsByCategory =
filterParams.getOrDefault("category", Collections.emptyList())
.stream()
.flatMap(category -> this.getProductsByCategory(category).stream())
.collect(Collectors.toCollection(HashSet::new));
Не следует предполагать, что toSet() возвращает изменяемый набор.
Возможно, будет проще уменьшить объем тернарного оператора: filterParams.containsKey("brand") ? filterParams.get("brand").stream() : Stream.empty() Или полностью избежать его: filterParams.getOrDefault("brand", Collections.singleton()).stream()
@shmosel еще раз хороший крик, хотя во втором примере я полагаю, вы имели в виду filterParams.getOrDefault("brand", Collections.emptyList()), а не Collections.singleton().
Попытка сделать его максимально основанным на лямбда-потоке.
Сначала текущий код вопроса вернет пустой набор, если какой-либо критерий (бренд или категория) не указан. Если это не ошибка, а предполагаемое поведение, тогда имеет смысл проверить, так ли это, и быстро вернуть пустой набор. Итак, код может начинаться так:
final List<String> filterBrands = filterParams.getOrDefault("brand", Collections.emptyList()));
final List<String> filterCategories = filterParams.getOrDefault("categories", Collections.emptyList()));
if (filterCategories.isEmpty() || filterCategories.isEmpty()) {
return Collections.emptySet();
}
Вы можете избежать составления второго списка, если первый пуст, но при этом добавляется несколько строк кода, и выигрыш, скорее всего, будет очень незначительным.
На этом этапе вы можете использовать другой подход к ответу, состоящий в создании набора продуктов с двумя критериями, а затем использовать пересечение. Однако, поскольку вы можете получить доступ к бренду, также известному как производство, непосредственно из Продукта, существует альтернатива, которая не требует явного создания экземпляров таких наборов, мы могли бы улучшить производительность в зависимости от соотношения размера прибыли по сравнению с каждым набором критериев. Итак, если результатом будет всего несколько продуктов ... скажем, 5, но каждый критерий или один из критериев может оказаться несколько тысяч, вы можете сэкономить память или перезарядить процессор, выполнив следующие действия:
final Set<String> manufacturers = filterBrands.stream()
.map(String::toLowerCase)
.collect(Collectors.toSet());
return filterCategories.stream()
.flatMap(this::getProductsByCategory)
.distinct() // optional but might be better performance
// if categories share products.
.filter(product -> manufacturers.contains(product.getManufacturer().toLowerCase()))
.collect(Collectors.toSet());
Не могли бы вы добавить API для продукта? Есть ли в нем ссылка на их бренд или категорию / категории? Я предполагаю, что у продукта только одна марка, но у него может быть несколько категорий.