Как исправить код записи с Java API потока 8? - PullRequest
0 голосов
/ 02 июня 2018

Я изучаю 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")).

Ответы [ 2 ]

0 голосов
/ 02 июня 2018

Попытка здесь сделать его как можно более лямбда-потоком.

Сначала текущий код вопроса вернет пустой набор, если какой-либо критерий (бренд или категория) опущен.Если это не ошибка, а предполагаемое поведение, то имеет смысл проверить, так ли это, и быстро вернуть пустой набор.Таким образом, код может начинаться следующим образом:

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());
0 голосов
/ 02 июня 2018

Если код работает, зачем его менять?тем не менее, вот решение с использованием потокового 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));
...