如何在java流收集操作中实现对象名称相同的数量汇总对象列表,避免toMap

问题描述 投票:0回答:1

如果对象的名称匹配,我正在尝试获取对象数量的总和。

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 的预期输出如下,但我需要在 listFromCollect 中获取它。请帮助我了解问题所在

[InventoryDetail(名称=苹果,数量=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) *

如果名称相同,请帮助我以一种干净的方式获取带有总数量的 InventorySku 列表。

java java-stream classcastexception collect accumulator
1个回答
0
投票
您似乎在累加器函数的

if

 分支中犯了一个拼写错误:

list.add(inventoryDetails);
应该是

list.add(newInventorySkuDetail);
否则,您将向列表添加 

Arrays.ArrayList

 ,但您希望列表仅包含 
InventoryDetail
 。这就是抛出 
ClassCastException
 的原因。

如果您添加了正确的内容,则根本不需要进行转换。您可以完全删除

objectToInventorySkuDetailsFunction

您的

combiner

功能也不正确。您应该将整个 
l2 合并
l1
 中,但是您忽略了 
l2
 中不存在于 
l1
 中的所有项目。

无论如何,出于学习目的,我会收集到

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)
即可。

© www.soinside.com 2019 - 2024. All rights reserved.