如何在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

提问人:Ramkumar SP 提问时间:10/30/2023 最后编辑:ProgmanRamkumar SP 更新时间:10/30/2023 访问量:30

问:

如果对象的名称匹配,我正在尝试将对象的数量相加

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 列表。

java java-stream classcastexception 收集 累加器

评论

1赞 Sweeper 10/30/2023
使用有什么问题?正如你所看到的,在这里使用变得非常混乱。toMapcollect
0赞 Ramkumar SP 10/30/2023
toMap 没有错,出于学习目的,想知道如何以一种困难/混乱的方式实现它

答:

1赞 Sweeper 10/30/2023 #1

您似乎在累加器函数的分支中犯了一个错别字:if

list.add(inventoryDetails);

应该是

list.add(newInventorySkuDetail);

否则,您将向列表中添加 an,但您希望列表仅包含 s 。这就是抛出的原因。Arrays.ArrayListInventoryDetailClassCastException

如果你添加了正确的东西,你根本不需要投射。您可以完全删除。objectToInventorySkuDetailsFunction

您的函数也不正确。您应该将整个 合并到 ,但您忽略了 中不存在的所有项目。combinerl2l1l2l1

无论如何,出于学习目的,我会收集到 a 而不是 .编写累加器和合路器更容易(您可以只使用 )。MapListmerge

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();

请注意,这两种方法都会更改现有对象。我在上面的实现中更改了它以创建新对象。InventoryDetailInventoryDetail

另请注意,您的代码使用不需要第二个流。您可以使用以下命令获取映射的值:toMapvalues()

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();

如果需要将其转换为 ,只需执行 。Listnew ArrayList<>(listFromToMap)