提问人:Ramkumar SP 提问时间:10/30/2023 最后编辑:ProgmanRamkumar SP 更新时间:10/30/2023 访问量:30
如何在java流收集操作中实现对象名称相同的数量汇总对象列表,避免toMap
How to achieve an quantity summed up object list where the name of object is same in java stream collect operation avoiding toMap
问:
如果对象的名称匹配,我正在尝试将对象的数量相加。
POJO 详细信息如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class InventoryDetail {
private String name;
private int quantity;
}
代码如下:
public class InventoryDetailsCollationUpdateQuantityForSameName {
public static void main(String[] args) {
List<InventoryDetail> inventoryDetails = Arrays.asList(
new InventoryDetail("iPhone", 1),
new InventoryDetail("Samsung", 1),
new InventoryDetail("iPhone", 2),
new InventoryDetail("Motorolla", 1),
new InventoryDetail("Nokia", 1),
new InventoryDetail("iPhone", 1)
);
Function<Object, InventoryDetail> objectToInventorySkuDetailsFunction = o -> (InventoryDetail) o;
//This works but want to know the other way to do with the .collect
List<InventoryDetail> listFromToMap = inventoryDetails.stream()
.map(objectToInventorySkuDetailsFunction)
.collect(Collectors.toMap(InventoryDetail::getName,
Function.identity(),
(is1, is2) -> {
is1.setQuantity(is1.getQuantity() + is2.getQuantity());
return is1;
}))
.entrySet().stream()
.map(stringInventoryDetailEntry -> stringInventoryDetailEntry.getValue())
.collect(Collectors.toList());
System.out.println(listFromToMap);
ArrayList<Object> listFromCollect = inventoryDetails.stream().collect(ArrayList::new,
(list, newInventorySkuDetail) -> {
if (list.isEmpty() || list.stream().map(objectToInventorySkuDetailsFunction)
.noneMatch(existingInventorySkuDetail -> StringUtils.equalsIgnoreCase(existingInventorySkuDetail
.getName(), newInventorySkuDetail.getName()))) {
list.add(inventoryDetails);
} else {
list.stream()
.map(objectToInventorySkuDetailsFunction)
.filter(existingInventorySkuDetail -> StringUtils.equalsIgnoreCase(existingInventorySkuDetail
.getName(), newInventorySkuDetail.getName()))
.forEach(existingInventorySkuDetail -> existingInventorySkuDetail
.setQuantity(existingInventorySkuDetail.getQuantity() + newInventorySkuDetail.getQuantity()));
}
}
, (l1, l2) -> {
l2.stream()
.map(objectToInventorySkuDetailsFunction)
.forEach(inventorySkuDetailToBeChecked -> {
if (l1.stream().map(objectToInventorySkuDetailsFunction)
.anyMatch(areInventorySkuDetailSame(inventorySkuDetailToBeChecked))) {
l1.stream()
.map(objectToInventorySkuDetailsFunction)
.filter(areInventorySkuDetailSame(inventorySkuDetailToBeChecked))
.forEach(existingInventorySkuDetail -> existingInventorySkuDetail
.setQuantity(existingInventorySkuDetail.getQuantity() + inventorySkuDetailToBeChecked.getQuantity()));
}
});
}
);
System.out.println(listFromCollect);
}
private static Predicate<InventoryDetail> areInventorySkuDetailSame(InventoryDetail newInventoryDetail) {
return existingInventoryDetail -> StringUtils.equalsIgnoreCase(existingInventoryDetail.getName(), newInventoryDetail.getName());
}
private static Consumer<InventoryDetail> addToQuantityAsNeeded(InventoryDetail inventoryDetailToBeChecked) {
return existingInventoryDetail -> existingInventoryDetail.setQuantity(existingInventoryDetail.getQuantity() + inventoryDetailToBeChecked.getQuantity());
}
}
listFromToMap 的预期输出如下,但我需要在 listFromCollection 中获取它。请帮助我了解问题所在
[InventoryDetail(名称=Apple,数量=4),InventoryDetail(名称=三星,数量=1),InventoryDetail(名称=诺基亚,数量=1),InventoryDetail(名称=摩托罗拉,数量=1)]
尝试运行上述代码时,出现以下错误 *线程“main”中的异常 java.lang.ClassCastException:java.util.Arrays$ArrayList 无法转换为 com.learning.java.pojo.InventoryDetail 在 com.learning.java.eight.stream.InventoryDetailsCollationUpdateQuantityForSameName.lambda$main$0(InventoryDetailsCollationUpdateQuantityForSameName.java:23) 在 java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) 在 java.util.ArrayList$ArrayListSpliterator.tryAdvance(ArrayList.java:1359) 在 java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:126) 在 java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:499) 在 java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:486) 在 java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) 在 java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:230) 在 java.util.stream.MatchOps$MatchOp.evaluateSequential(MatchOps.java:196) 在 java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) 在 java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459) 在 com.learning.java.eight.stream.InventoryDetailsCollationUpdateQuantityForSameName.lambda$main$6(InventoryDetailsCollationUpdateQuantityForSameName.java:42) 在 java.util.stream.ReduceOps$4ReducingSink.accept(ReduceOps.java:220) 在 java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) 在 java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482) 在 java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472) 在 java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) 在 java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) 在 java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:510) 在 com.learning.java.eight.stream.InventoryDetailsCollationUpdateQuantityForSameName.main(InventoryDetailsCollationUpdateQuantityForSameName.java:39) *
如果名称相同,请帮助我以一种干净的方式获取带有总和数量的 InventtorySku 列表。
答:
您似乎在累加器函数的分支中犯了一个错别字:if
list.add(inventoryDetails);
应该是
list.add(newInventorySkuDetail);
否则,您将向列表中添加 an,但您希望列表仅包含 s 。这就是抛出的原因。Arrays.ArrayList
InventoryDetail
ClassCastException
如果你添加了正确的东西,你根本不需要投射。您可以完全删除。objectToInventorySkuDetailsFunction
您的函数也不正确。您应该将整个 合并到 ,但您忽略了 中不存在的所有项目。combiner
l2
l1
l2
l1
无论如何,出于学习目的,我会收集到 a 而不是 .编写累加器和合路器更容易(您可以只使用 )。Map
List
merge
BiFunction<InventoryDetail, InventoryDetail, InventoryDetail> merge = (detail1, detail2) ->
new InventoryDetail(detail1.getName(), detail1.getQuantity() + detail2.getQuantity());
Collection<InventoryDetail> listFromCollect = inventoryDetails.stream().collect(
HashMap<String, InventoryDetail>::new,
(map, detail) -> map.merge(detail.getName(), detail, merge),
(m1, m2) -> m2.forEach((name, detail) -> m1.merge(name, detail, merge))
).values();
请注意,这两种方法都会更改现有对象。我在上面的实现中更改了它以创建新对象。InventoryDetail
InventoryDetail
另请注意,您的代码使用不需要第二个流。您可以使用以下命令获取映射的值:toMap
values()
BinaryOperator<InventoryDetail> merge = (detail1, detail2) ->
new InventoryDetail(detail1.getName(), detail1.getQuantity() + detail2.getQuantity());
Collection<InventoryDetail> listFromToMap = inventoryDetails.stream()
.collect(Collectors.toMap(
InventoryDetail::getName,
Function.identity(),
merge))
.values();
如果需要将其转换为 ,只需执行 。List
new ArrayList<>(listFromToMap)
评论
toMap
collect