我正在练习用Java处理简单的JSON数据,但是我有点卡住了。
因此,假设我是一名面包师,多年来我掌握了各种产品的数据。例如,我有三种产品:蛋糕,百吉饼和饼干。我的数据是每年收集的,因此示例JSON数据集可能类似于:
[ {"name": "cake", "consumers": 200, "tastiness": 8.0}, {"name": "cake", "consumers": 220, "tastiness": 8.3}, {"name": "bagel", "consumers": 1000, "tastiness": 6.4}, {"name": "bagel", "consumers": 1200, "tastiness": 7.5}, {"name": "bagel", "consumers": 800, "tastiness": 5.7}, {"name": "cookie", "consumers": 500, "tastiness": 9.6} ]
如您所见,有两个“蛋糕”条目表示蛋糕已经存在两年,有三个“百吉饼”条目表明面包圈已经存在三年,等等。
我想压缩此数据以获得每种产品的加权平均口味。例如,蛋糕的平均口味为(8.0 * 200 + 8.3 * 220)/(200 + 220)= 8.157,所以我希望我的新集合包含一个带有值(“ cake”,8.157)的数据条目,除了相应的百吉饼和饼干的平均口味。
解析JSON数据并提取我想要的值是微不足道的,但是我正在努力的是提取/压缩具有相同名称的数据值并获得平均口味的最佳方法。
到目前为止,我已经考虑过为名称,消费者编号和美味程度创建arrayLists,但是我意识到这可能有点混乱和不便。
我目前正在考虑制作一个单独的名为“产品”的类,其属性为“名称”,“消费者”和“美味”,然后创建一个单个 arrayList<Product>
。但是,我坚持如何最好地遍历产品的arrayList并获取具有相同名称的产品并计算加权平均值。
我知道,对于我的问题可能存在一种非常简单的解决方案,但目前还没有解决,因此,我们将不胜感激。谢谢:)
答案 0 :(得分:2)
因此,您要使用多种产品和任意数量的组。
您是否考虑过使用Map
?您可以使用类似Map<String, List<Product>>
您可以使用Java 8流(流List<Product>
)并按产品名称分组来构建地图。
最后,您可以遍历地图的keySet
和List
中的产品列表
示例:
List<Product> products = buildProductsList();
Map<String, List<Product>> productsGroupedByName = products.stream()
.collect(Collectors.groupingBy(Product::getName));
for(String name : productsGroupedByName.keySet()) {
System.out.println("The name is: " + name);
for (Product product : productsGroupedByName.get(name)) {
// do something with the products
}
}
答案 1 :(得分:2)
我认为创建具有上述属性的容器类的想法是一个好主意。
结合使用groupingBy
类中的reducing
和java.util.stream.Collectors
,可以实现这一目标。
我们首先定义一个将保存数据的类:
public class SugarStats {
private String name;
private long consumers;
private double tastiness;
// Constructor(name, consumers, tastiness) and getters left out for brevity
}
我们将使用此容器类为我们提供平均值,因此我们将添加一个方法:
public double getAverageTastiness() {
return this.tastiness / this.consumers;
}
此外,我假设已经完成了从JSON到POJO的转换,并且您的数据如下所示:
List<SugarStats> stats = Arrays.asList(
new SugarStats("cake", 200, 8.0),
new SugarStats("cake", 220, 8.3),
new SugarStats("bagel", 1000, 6.4),
new SugarStats("bagel", 1200, 7.5),
new SugarStats("bagel", 800, 5.7),
new SugarStats("cookie", 500, 9.6));
然后我们将执行以下操作:
我们要遍历列表:
stats.stream()
然后,我们将总结每个SugarStats
的总美味程度得分,而不是单个条目的得分:
.map(t -> new SugarStats(t.getName(), t.getConsumers(), t.getConsumers() * t.getTastiness()))
例如, cookie sugarstats实例现在的口味为 500 * 9.6 = 4800
。
然后,我们按名称分组收集结果。通常,这将返回一个以Map
为键的name
和一个带有所有值的List<SugarStats>
。但是,我们对List
不感兴趣,我们仅对平均值感兴趣。因此,我们提供了一个 reducing 下游收集器,该收集器收集所有SugarStats并将其组合在一起,从而得出消费者数量和口味的总和。
例如,new SugarStats("cake", 200, 1600.0) + new SugarStats("cake", 220, 1826.0)
将变为new SugarStats("cake", 420, 3426)
。
.collect(groupingBy(t -> t.getName(),
reducing(
new SugarStats("", 0, 0),
(p1, p2) -> new SugarStats(
p2.getName(),
p1.getConsumers() + p2.getConsumers(),
p1.getTastiness() + p2.getTastiness()
)
)
));
现在我们可以品尝到美味了
result.values().stream()
.forEach(t -> System.out.println(t.getName() + ": " + t.getAverageTastiness()));