【发布时间】:2020-02-08 17:49:34
【问题描述】:
我有一个方法可以计算我们从 API 请求调用中接收到的对象列表的营养成分。
方法如下:
public Nutrients nutrientsCalculator(DailyMeals dailyMeals) {
String foodNamesForRequest = prepareFoodNamesForRequest(dailyMeals);
HttpEntity<NutrientsBodyForRequest> requestBody = prepareRequestForAPICall(foodNamesForRequest);
ResponseEntity<List<FoodNutritional>> response =
//create request here
if (nonNull(response.getBody())) {
double totalFat = response.getBody()
.stream()
.map(FoodNutritional::getTotalFat)
.mapToDouble(Double::doubleValue)
.sum();
double totalProtein = response.getBody()
.stream()
.map(FoodNutritional::getProtein)
.mapToDouble(Double::doubleValue)
.sum();
double totalCarbohydrates = response.getBody()
.stream()
.map(FoodNutritional::getTotalCarbohydrate)
.mapToDouble(Double::doubleValue)
.sum();
double totalDietaryFiber = response.getBody()
.stream()
.map(FoodNutritional::getDietaryFiber)
.mapToDouble(Double::doubleValue)
.sum();
return Nutrients.builder()
.carbohydrates(totalCarbohydrates)
.protein(totalProtein)
.fat(totalFat)
.dietaryFiber(totalDietaryFiber)
.build();
}
return new Nutrients();
}
我的 FoodNutritional.class 看起来像:
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
class FoodNutritional {
@JsonProperty("food_name")
private String foodName;
@JsonProperty("brand_name")
private String brandName;
@JsonProperty("serving_qty")
private Integer servingQuantity;
@JsonProperty("serving_unit")
private String servingUnit;
@JsonProperty("serving_weight_grams")
private String servingWeightGrams;
@JsonProperty("nf_calories")
private Double calories;
@JsonProperty("nf_total_fat")
private Double totalFat;
@JsonProperty("nf_saturated_fat")
private Double saturatedFat;
@JsonProperty("nf_cholesterol")
private Double cholesterol;
@JsonProperty("nf_sodium")
private Double sodium;
@JsonProperty("nf_total_carbohydrate")
private Double totalCarbohydrate;
@JsonProperty("nf_dietary_fiber")
private Double dietaryFiber;
@JsonProperty("nf_sugars")
private Double sugars;
@JsonProperty("nf_protein")
private Double protein;
@JsonProperty("nf_potassium")
private Double potassium;
}
我的方法解决方案有效,但我开始思考是否有可能摆脱这种方法的 sum 流方法样板。
我想要实现的只是对单个字段求和:totalFat、protein、dietaryFiber、totalCarbohydrate,并将它们作为新对象的组件字段返回。
对于如何提高当前版本代码质量的建议,我将不胜感激。
编辑:
周末我花了一点时间找到了一种不同的附加方法,它可以满足要求并且功能更强大。最后,我创建了两个静态方法,例如:
private static Nutrients reduceNutrients(Nutrients n1, Nutrients n2) {
return Nutrients.builder()
.protein(n1.getProtein() + n2.getProtein())
.carbohydrates(n1.getCarbohydrates() + n2.getCarbohydrates())
.dietaryFiber(n1.getDietaryFiber() + n2.getDietaryFiber())
.fat(n1.getFat() + n2.getFat())
.build();
}
private static Nutrients fromFoodNutritionalToNutrients(FoodNutritional foodNutritional) {
return Nutrients.builder()
.dietaryFiber(foodNutritional.getDietaryFiber())
.carbohydrates(foodNutritional.getTotalCarbohydrate())
.fat(foodNutritional.getTotalFat())
.protein(foodNutritional.getProtein())
.build();
}
毕竟我是这样使用它的:
Stream<FoodNutritional> foodNutritionalStream = Optional.ofNullable(response.getBody()).stream()
.flatMap(List::stream);
Nutrients nutrients = foodNutritionalStream
.map(NutrientsCalculatorService::fromFoodNutritionalToNutrients)
.reduce(NutrientsCalculatorService::reduceNutrients)
.orElseThrow(() -> new CustomException("custom_exception");
不过,还是要感谢 @Koziołek、@Nir Levy 和 @Naman 成为我的缪斯女神。感谢每一个承诺。
【问题讨论】:
-
看看Java流中的reduction。您必须创建一个零初始化标识(初始状态/累加器),然后使用将脂肪、蛋白质等添加到累加器的二进制操作来减少流。
-
@madhead 另一种有趣的方法,我肯定会检查这种方法,因为我还没有使用这个 Collectors 方法。
-
它已经在这里了,在Nir Levy's answer。恕我直言,它是性能最高的,因为它只会迭代您的收藏一次,总结所有营养。
-
@madhead 当我写这个答案时,我还没有看到这个答案。现在,在一瞬间,我收到了多种选择。再次感谢您的承诺。
标签: java lambda java-8 functional-programming java-stream