【bug】 -- List.subList()未序列化导致Redis报错

别人遇到的bug,收集起来以防自己同样犯错。

bug产生的场景:

项目中对文章的详情内容通过Redis做了缓存,详情中以List形式关联了一些其他内容。在Redis中存储数据也将会是从业务中获取的List集合。存储数据要求3条即可,但是查询接口返回的数据不一定为3条,当多于3条是,就需要截取,在截取的过程中使用了list.subList(0,3),来获取其中3条数据。之后就报错了。。。 (由于这块业务的负责人是另一位同事,自己没有看到异常信息,无法截图,故遇到时能够想起即可)

原错误代码: 

分析:

切割后的存储形式应该就变成了java.util.ArrayList$SubList,这种格式的对象从Redis中拿出来是没法进行反序列化的。原因是list接口并没有实现序列化。在jdk6中,List<E> subList(int fromIndex, int toIndex)返回的是没有实现Serializable接口的java.util.RandomAccessSubList。在其父类或者接口中都找不到Serializable这个标志。而在jdk7和8中,sublist实现,它是在ArrayList中直接搞了一个私有内部类:private class SubList extends AbstractList<E> implements RandomAccess,而jdk6class RandomAccessSubList<E> extends SubList<E> implements RandomAccess,注意这里的SubList跟上面的是不同的,这里的SubList是AbstractList的内部类。

综上,ArrayList.subList方法返回的对象是一个sublist类型的视图,这个sublist类型的是ArrayList的一个内部类,不支持序列化。视图的含义就是它里面的元素数量变了,但是操作其中的元素实际上还是操作的原来的list,并不是新的那份list。如果改变原有的list,那么就会抛出ConcurrentModificationException异常。即:

 

就算未对原有list进行修改,但是因其未做序列化,从Redis中读取数据时会因无法反序列化而出现问题。

解决方案:

1.重新创建一个可以实现序列化的list集合,将截取后的list存入,实现序列化

List list = new ArrayList(imgList.subList(0,3));
jsonRelatedArticles.setImgList(list);

  or

List<XXX> serializedList= new ArrayList<XXX>();  
serializedList.addAll(sublist); 
//在这里就是,其实跟上边一种大同小异
List<JsonArticleDetail.JsonImg> serializedList= new ArrayList<>();  
serializedList.addAll(imgList.subList(0,3)); 
jsonRelatedArticles.setImgList(serializedList);

 2.避免使用subList,使用循环遍历一个个add。


if (a.getImgList() != null && a.getImgList().size() >= 3){
    List<JsonArticleDetail.JsonImg> imgList = new ArrayList<>(3);
	for (int i = 0; i < 3; i++){
		JsonArticleDetail.JsonImg jsonImg = new JsonArticleDetail.JsonImg();							 
        jsonImg.setUrl(imageUtil.getCommonImageUrl(a.getImgList().get(i).getImgPath()));
		imgList.add(jsonImg);
   }
}
//注意a.getImgList().get(i).getImgPath()可能会有空指针异常,本代码只简要表明重点,视情况判断。

3.其实单针对于本次错误代码而言,只需要在原有循环上简单修改即可。如下:

综上:

发现简单一个subList就有很大的陷阱,所以基础知识还是需要牢固。

附:java.util.List.subList使用注意

redis数据存储形式:

 ridis大方面来说,就是一个K-V数据库,用于存储数据,这个数据库也可以存储复杂结构的数据,像有关的集合数据,所以它也具备了对复杂数据的高效查询,(故障恢复特性,文件有关数据持久化能力)

数据跨平台存储通过网络的传输层进行传输,所以必须序列化

 

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值