关于ArrayList中的retainAll的一些思考

思考

我们都知道,比较两个对象的是否相同,一般是先通过hashcode方法比较hash值是否相等,如果相同(哈希碰撞)然后通过equals进行比较各个属性值是否相同,如果都相同,那么才是真正的相同。

我们看一个使用retainAll更新OrderItem的方法:

private boolean intersection(boolean flag,
                                 List orderItemVoStatisticsList,
                                 List orderItemVoTemporaryList) {
        //1、求交集
        List listIntersection = new ArrayList<>(orderItemVoTemporaryList);
        listIntersection.retainAll(orderItemVoStatisticsList);
        if (!EmptyUtil.isNullOrEmpty(listIntersection)) {
            // 交集中的订单项 转map ==> key: dishId    value: num
            Map dishIdAndNum = listIntersection.stream().collect(
                    Collectors.toMap(OrderItemVo::getDishId, OrderItemVo::getDishNum));
            // 在mysql的订单项中,找到交集中所包含菜品的订单项
            List 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;

    @Builder
    public 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;

    @Override
    public 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);
    }

    @Override
    public int hashCode() {
        return Objects.hash(dishId);
    }
}

很明显,这里重写了hashCode和equals方法,而且很巧妙的是他只是对dishId进行了hash运算和equals比较,这样只要是相同的菜品,那么我就可以取其交集,进行之后的逻辑操作!

所以当我们使用retainAll对两个集合取交集时,要确定我们集合中的泛型是否重写了针对指定属性重写了hash和equals方法,否则将会取交失败。

看一下ArrayList中的retainAll源码

关于ArrayList中的retainAll的一些思考_第1张图片

重点分析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 survivors
        for (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。

  1. 首先,函数需要检查集合C是否为空,如果为空则抛出NullPointerException异常。
  2. 然后,它使用一个Object类型的数组elementData存储集合中的所有元素。
  3. 接下来,函数使用循环遍历数组元素[from, end)范围内的所有元素,检查它们是否在集合C中。
  4. 如果在,而且complement为false,说明需要删除这个元素,否则需要保留这个元素。
  5. 随后,函数使用第二个循环遍历数组剩余的元素再次检查它们是否在集合C中。
  6. 如果在,而且complement为true,说明需要删除这个元素,否则需要保留
  7. 如果遇到异常,函数会回滚到前一个保留的元素后的元素前移动。
  8. 最后,函数更新修改计数器modCount,并通过shiftTailOverGap函数将删除的元素从数组中移除。
  9. 最后,函数返回一个布尔值,指示操作是否成功。

你可能感兴趣的:(JAVA,java,数据库,redis)