开发过程中遇到的坑
开发过程经常会使用subList做分页处理。
比如下面的代码
while(pageIndex < maxSize) {
List temp = userIds.subList(pageIndex, (pageIndex + pageSize) > maxSize ? maxSize : (pageIndex + pageSize));
processWechatReserve(temp, unBingdingUserIdList, trueLiveId);
}
private void processWechatReserve(List tempUserIdList, List unBingdingUserIdList, Long targetId){
if (CollectionUtils.isNotEmpty(tempUserIdList) && CollectionUtils.isNotEmpty(unBingdingUserIdList)) {
tempUserIdList.removeAll(unBingdingUserIdList);
当变量unBingdingUserIdList有内容时,
这段代码就会报错IndexOutOfBoundsException。
其实写代码时大家都知道subList是原List的一个视图,
对subList的操作会体现到原List上。
但万万没想到的是调用的方法签名是这样的
processWechatReserve(List, List){
也就是说,实现processWechatReserve方法的人可能并不知道List的来源是subList,此时就很容易出错。
就着本次的问题,萌新也总结了一下使用subList的一些注意事项。
一些例子
1. 元素范围
List subList(int fromIndex, int toIndex)
该方法所取的元素下标为fromIndex至toIndex-1
public List subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
SubList(AbstractList parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
parentOffset就是指向原list
2. 修改
- 父子list做的非结构性修改(non-structural changes)都会影响到彼此:所谓的“非结构性修改”,是指不涉及到list的大小改变的修改。相反,结构性修改,指改变了list大小的修改。
List list = Lists.newArrayList(1, 3, 5);
List subList = list.subList(0, 1);
subList.set(0, -1);
System.out.println(list);
System.out.println(subList);
[-1, 3, 5]
[-1]
- 对于结构性修改,子list的所有操作都会反映到父list上。但父list的修改将会导致返回的子list失效。
List list = Lists.newArrayList(1, 3, 5);
List subList = list.subList(0, 1);
subList.remove(0);
System.out.println(list);
System.out.println(subList);
[3, 5]
[]
List list = Lists.newArrayList(1, 3, 5);
List subList = list.subList(0, 1);
list.remove(0);
System.out.println(list);
System.out.println(subList);
[3, 5]
对subList对访问会报异常ConcurrentModificationException
原因:
原list的modCount为4,而subList的modCount为3。
3. 如何删除list中的某段数据:
list.subList(from, to).clear();
如何避免
如果需要对subList作出修改,又不想动原list。那么可以创建subList的一个拷贝
subList = Lists.newArrayList(subList);
list.stream().skip(strart).limit(end).collect(Collectors.toList());
此刻,我竟然想到了《阿里巴巴Java开发手册》上面有两个提醒与此相关。
纸上得来终觉浅,绝知此事要躬行。
哭出声。。。
2. 【强制】ArrayList的subList结果不可强转成ArrayList,
否则会抛出ClassCastException 异常,
即java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。
说明:subList 返回的是 ArrayList 的内部类 SubList,
并不是 ArrayList 而是 ArrayList 的一个视图,
对于 SubList 子列表的所有操作最终会反映到原列表上。
3. 【强制】在 subList 场景中,高度注意对原集合元素的增加或删除,
均会导致子列表的遍历、 增加、删除产生ConcurrentModificationException 异常。