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

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

bug产生的场景:

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

原错误代码: 

【bug】 -- List.subList()未序列化导致Redis报错_第1张图片

分析:

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

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

【bug】 -- List.subList()未序列化导致Redis报错_第2张图片

 

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

解决方案:

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

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

  or

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

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


if (a.getImgList() != null && a.getImgList().size() >= 3){
    List 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.其实单针对于本次错误代码而言,只需要在原有循环上简单修改即可。如下:

【bug】 -- List.subList()未序列化导致Redis报错_第3张图片

综上:

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

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

redis数据存储形式:

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

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

 

你可能感兴趣的:(【bug】 -- List.subList()未序列化导致Redis报错)