Java中list.subList()方法的一次爬坑
说明:
提示:这篇文章仅是记录自己成长路上的愚笨,不喜勿喷,谢谢!!
问题描述:
Java中的subList方法平时使用的不多,但是一次项目中由于向数据库中插入的数据太大,使用了一次该方法。原方法是这样的:
int i = 0;
int toIndex = 0;
while (i <= areas.size()) {
toIndex = (i + 50000) <= areas.size() ? (i + 50000) : areas.size();
List<SysArea> subList = areas.subList(i, toIndex);
int insertRows = sysAreaMapper.batchInsert(subList);
if (insertRows != toIndex - i) {
throw new CustomException("数据库插入的条目数不符");
}
subList.clear();
i += 50000;//在截取了以后,想当然的要把fromIndex向后移
}
提示:
1、注意一下这段代码:subList.clear();
2、areas:是一个ArrayList,数据量很大。
3、数据库设置了自动commit。
问题: 就是数据库中插入的数据是原areas中个list的数据量的 一半 。
原因分析:
通过看Java的源码才知道subList()方法返回原来list的从[fromIndex, toIndex)之间这一部分的视图,之所以说是视图,是因为实际上,返回的list是靠原来的list支持的。
所以,你对原来的list和返回的list做的“结构性修改”(structural changes),都会影响到彼此对方。
所谓的“非结构性修改”,是指不涉及到list的大小改变的修改。相反,结构性修改,指改变了list大小的修改。
Returns a view of the portion of this list between the specified
{@code fromIndex}, inclusive, and {@code toIndex}, exclusive. (If
{@code fromIndex} and {@code toIndex} are equal, the returned list is
empty.) The returned list is backed by this list, so non-structural
changes in the returned list are reflected in this list, and vice-versa.
The returned list supports all of the optional list operations.
<p>This method eliminates the need for explicit range operations (of
the sort that commonly exist for arrays). Any operation that expects
a list can be used as a range operation by passing a subList view
instead of a whole list. For example, the following idiom
removes a range of elements from a list:
<pre>
list.subList(from, to).clear();
</pre>
Similar idioms may be constructed for {@link #indexOf(Object)} and
{@link #lastIndexOf(Object)}, and all of the algorithms in the
{@link Collections} class can be applied to a subList.
<p>The semantics of the list returned by this method become undefined if
the backing list (i.e., this list) is <i>structurally modified</i> in
any way other than via the returned list. (Structural modifications are
those that change the size of this list, or otherwise perturb it in such
a fashion that iterations in progress may yield incorrect results.)
@throws IndexOutOfBoundsException {@inheritDoc}
@throws IllegalArgumentException {@inheritDoc}
以上是Java源码的说明文档,其中有这样一句话:
Any operation that expects a list can be used as a range operation by passing a subList view
instead of a whole list.
For example, the following idiom removes a range of elements from a list:
<pre>
list.subList(from, to).clear();
</pre>
意思是说:通过subList 获取的范围视图的操作会影响整个list列表! 人家的例子正好是举的 clear()方法。。。
注意:源码中还有这样一句代码
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
这个方法会在操作原list的时候检查原list是否被修改过,如果被修改过就会报错!
验证:
@Test
public void test1() {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(i);
}
int step = 3;//截取的步长
while (list.size() > 0) {
int toIndex = list.size() <= step ? list.size() : step;
List<Integer> subList = list.subList(0, toIndex);
System.out.println(JSON.toJSONString(subList));
subList.clear();
System.out.println("原list列表的长度变为:"+list.size());
}
}
控制台输出的结果:
com.bjxst.common.JavaTest,test1
[0,1,2]
原list列表的长度变为:7
[3,4,5]
原list列表的长度变为:4
[6,7,8]
原list列表的长度变为:1
[9]
原list列表的长度变为:0
Process finished with exit code 0
tips:
如果想删除一个list的某个区段,比如删除list的第4-6个元素, 可以利用sublist的视图操作影响原来的list的这个特性,比如
list.subList(3, 6).clear();
这样就可以了。