如果对象的名称匹配,我正在尝试获取对象的数量的总和。
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 列表。
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)
即可。