关于ArrayList中的retainAll的一些思考

思考

我们都知道,比较两个对象的是否相同,一般是先通过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: num
            Map<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;

    @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源码

重点分析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. 最后,函数返回一个布尔值,指示操作是否成功。

### 回答1: 好的,下面是关于ArrayList的一些信息: ArrayListJava的一个动态数组类,它实现了List接口,能够动态地添加、删除元素,并且可以自动扩展容量。ArrayList的元素可以是任何对象,包括null。 在ArrayList,元素的位置是由它们在数组的索引决定的。它支持随机访问,也就是说可以通过索引值来获取或更新元素。此外,ArrayList还提供了一些便捷的方法,如add、remove和size等。 需要注意的是,ArrayList不是线程安全的,如果多个线程同时对它进行操作,可能会导致不可预测的结果。如果需要在多线程环境使用动态数组,可以考虑使用Vector类或者在对ArrayList进行操作时进行同步处理。 ### 回答2: ArrayListJava的一种数据结构,它可以动态地存储和管理一组对象。 ArrayList具有以下特点: 1. 动态大小:与传统的数组不同,ArrayList的大小是可以动态调整的。当需要添加新元素时,ArrayList会自动扩展其容量。同样地,当需要删除元素时,ArrayList会自动收缩其容量。 2. 随机访问:ArrayList的元素可以通过索引进行随机访问。这意味着我们可以通过指定索引的方式快速地访问或更新特定位置的元素。 3. 具有重复元素:ArrayList可以包含重复的元素。这意味着我们可以向ArrayList添加相同的元素多次。 4. 支持迭代:ArrayList实现了Java的Iterable接口,因此我们可以使用迭代器(Iterator)来遍历ArrayList的元素。 5. 自动装箱/拆箱:ArrayList可以自动处理基本数据类型和其对应的包装类型之间的转换。这意味着我们可以将int、char等基本数据类型直接添加到ArrayList,而不需要手动进行转换。 6. 线程不安全:ArrayList是非线程安全的。这意味着当多个线程同时访问和修改ArrayList时,可能会发生竞态条件和数据不一致的问题。在多线程环境下,应该使用线程安全的集合类,如Vector或CopyOnWriteArrayList。 尽管ArrayList有很多优点,但也有一些缺点。例如,在删除或插入元素时,需要移动其他元素,这可能会导致性能下降。此外,ArrayList的大小只能受限于可用内存大小。 总之,ArrayList是一个非常有用的数据结构,适用于需要频繁对元素进行添加、删除、修改和随机访问的场景。同时,我们也应该根据具体的需求和性能要求选择合适的集合类。 ### 回答3: ArrayListJava常用的数据结构,它是基于数组实现的动态数组。它能够自动调整大小,可以存储任意类型的对象。 ArrayList具有以下特点: 1. 可变长度:与数组不同,ArrayList的长度可以动态增长或缩小。当元素超出当前容量时,会自动进行扩容。这种自动调整大小的功能使得ArrayList非常灵活,适合处理大小不确定的数据集合。 2. 快速访问:ArrayList通过下标索引来访问元素,因此可以快速地进行读取和修改操作。相比链表等其他数据结构,ArrayList的访问速度更快。 3. 有序存储:ArrayList按照元素的插入顺序进行存储,且可以保持元素的有序性。这样,可以方便地进行元素的查找和排序等操作。 4. 允许重复元素:ArrayList允许存储重复的元素。当需要统计某个元素的出现次数或保留所有重复元素时,ArrayList是一个很好的选择。 5. 内存占用:由于ArrayList是动态数组,会预留一些额外的空间用于扩容。因此,如果元素数量明确,或者预知某个ArrayList不会太大,可以使用指定初始容量来减少内存占用。 总之,ArrayListJava非常常用的数据结构,它具有动态大小、快速访问、有序存储和允许重复元素等特点。在编写Java程序时,我们可以根据实际需求选择合适的数据结构,包括ArrayList,来方便地进行数据的存储、操作和访问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.huang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值