思考
我们都知道,比较两个对象的是否相同,一般是先通过hashcode方法比较hash值是否相等,如果相同(哈希碰撞)然后通过equals进行比较各个属性值是否相同,如果都相同,那么才是真正的相同。
我们看一个使用retainAll更新OrderItem的方法:
private boolean intersection(boolean flag,List<OrderItemVo> orderItemVoStatisticsList,List<OrderItemVo> orderItemVoTemporaryList) {//1、求交集List<OrderItemVo> listIntersection = new ArrayList<>(orderItemVoTemporaryList);listIntersection.retainAll(orderItemVoStatisticsList);if (!EmptyUtil.isNullOrEmpty(listIntersection)) {// 交集中的订单项 转map ==> key: dishId value: numMap<Long, Long> dishIdAndNum = listIntersection.stream().collect(Collectors.toMap(OrderItemVo::getDishId, OrderItemVo::getDishNum));// 在mysql的订单项中,找到交集中所包含菜品的订单项List<OrderItem> orderItemList = orderItemVoStatisticsList.stream()// 找到交集中所包含菜品的订单项.filter(orderItemVo -> dishIdAndNum.containsKey(orderItemVo.getDishId()))// 封装为OrderItem.map(orderItemVo -> OrderItem.builder().id(orderItemVo.getId())//mysql中菜品数量 + redis中对应菜品数量.dishNum(orderItemVo.getDishNum() + dishIdAndNum.get(orderItemVo.getDishId())).build())// 收集到批量集合中 进行修改.collect(Collectors.toList());flag = orderItemService.updateBatchById(orderItemList);}return flag;}
这里使用retainAll方法去取交集
那么它是怎么判断两个对象是否相同的呢?就算我redis购物车订单项和mysql数据库订单项的点的菜是一样的,但是可能菜的数量,菜的口味还不一样呢,甚至还有其他属性不同等等,那么应该怎么实现呢?
看一下OrderItemVo是否重写了hashCode和equals方法
/*** @ClassName OrderItemVo.java* @Description 订单项*/
@Data
@NoArgsConstructor
public class OrderItemVo extends BasicVo {private static final long serialVersionUID = 1L;@Builderpublic OrderItemVo(Long id, Long productOrderNo, BigDecimal memberDiscount, BigDecimal discountAmount,Long dishId, String dishName, String categoryId, BigDecimal price, BigDecimal reducePrice,Long dishNum,String dishFlavor){super(id);this.productOrderNo=productOrderNo;this.memberDiscount=memberDiscount;this.discountAmount=discountAmount;this.dishId=dishId;this.dishName=dishName;this.categoryId=categoryId;this.price=price;this.reducePrice=reducePrice;this.dishNum=dishNum;this.dishFlavor = dishFlavor;}@ApiModelProperty(value = "业务系统订单号【分表字段】")@JsonFormat(shape = JsonFormat.Shape.STRING)private Long productOrderNo;@ApiModelProperty(value = "会员折扣")private BigDecimal memberDiscount;@ApiModelProperty(value = "优惠金额")private BigDecimal discountAmount;@ApiModelProperty(value = "菜品ID")@JsonFormat(shape = JsonFormat.Shape.STRING)private Long dishId;@ApiModelProperty(value = "菜品名称")private String dishName;@ApiModelProperty(value = "菜品分类id")@JsonFormat(shape = JsonFormat.Shape.STRING)private String categoryId;@ApiModelProperty(value = "菜品价格")private BigDecimal price;@ApiModelProperty(value = "菜品优惠价格")private BigDecimal reducePrice;@ApiModelProperty(value = "菜品数量")private Long dishNum;@ApiModelProperty(value = "菜品附件信息")private FileVo fileVo;@ApiModelProperty(value = "菜品口味")private String dishFlavor;@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;OrderItemVo that = (OrderItemVo) o;return dishId.equals(that.dishId);}@Overridepublic int hashCode() {return Objects.hash(dishId);}
}
很明显,这里重写了hashCode和equals方法,而且很巧妙的是他只是对dishId进行了hash运算和equals比较,这样只要是相同的菜品,那么我就可以取其交集,进行之后的逻辑操作!
所以当我们使用retainAll对两个集合取交集时,要确定我们集合中的泛型是否重写了针对指定属性重写了hash和equals方法,否则将会取交失败。
看一下ArrayList中的retainAll源码
重点分析BatchRemove方法
boolean batchRemove(Collection<?> c, boolean complement,final int from, final int end) {//集合C不能为空Objects.requireNonNull(c);//定义一个数组final Object[] es = elementData;int r;// Optimize for initial run of survivorsfor (r = from;; r++) {if (r == end)return false;if (c.contains(es[r]) != complement)break;}int w = r++;try {for (Object e; r < end; r++)if (c.contains(e = es[r]) == complement)es[w++] = e;} catch (Throwable ex) {// Preserve behavioral compatibility with AbstractCollection,// even if c.contains() throws.System.arraycopy(es, r, es, w, end - r);w += end - r;throw ex;} finally {modCount += end - w;shiftTailOverGap(es, w, end);}return true;}
这段代码实现批量删除,即从集合C中删除所有在数组元素[from, end)范围内的元素(根据布尔值complement参数决定是删除还是保留这些元素)这里第true。
- 首先,函数需要检查集合C是否为空,如果为空则抛出NullPointerException异常。
- 然后,它使用一个Object类型的数组elementData存储集合中的所有元素。
- 接下来,函数使用循环遍历数组元素[from, end)范围内的所有元素,检查它们是否在集合C中。
- 如果在,而且complement为false,说明需要删除这个元素,否则需要保留这个元素。
- 随后,函数使用第二个循环遍历数组剩余的元素再次检查它们是否在集合C中。
- 如果在,而且complement为true,说明需要删除这个元素,否则需要保留
- 如果遇到异常,函数会回滚到前一个保留的元素后的元素前移动。
- 最后,函数更新修改计数器modCount,并通过shiftTailOverGap函数将删除的元素从数组中移除。
- 最后,函数返回一个布尔值,指示操作是否成功。