您的位置:首页 > 编程语言 > Java开发

java ArrayList补充——subList

2015-12-02 16:22 513 查看
首先看一个关于subList的问题:下面这段代码的输出结果是什么?

List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);

List<Integer> sub = list.subList(0, 2);
sub.add(5);

for (Integer i : list) {
System.out.print(i.intValue() + "\t");
}


如果认为答案是1 2 3,那么就错了,正确答案是1 2 5 3

那么问题是,为什么对sub插入的数据同时会影响到原本的list中

下面从源码角度解释这个问题:

subList方法的确会产生一个新的List不错,然而这个新的List并不完全是一个新的List,也就是说,新的List和原先的List并没有完全脱离,而是通过偏移量对原本的List进行管理,当执行add操作时候,依旧是插入到原先的List当中。

subList方法并不是ArrayList中的方法,而是其父类AbstractList中提供的方法。

它的源码如下:

public List<E> subList(int fromIndex, int toIndex) {
return (this instanceof RandomAccess ?
new RandomAccessSubList<E>(this, fromIndex, toIndex) :
new SubList<E>(this, fromIndex, toIndex));
}


如果说调用此方法的List是一个RandomAccess实例,将返回一个RandomAccessSubList,否则返回一个SubList。

RandomAccessSubList与SubList均是AbstractList的内部类,而前者很简单,它是后者的子类,区别在于实现了RandomAccess接口,那么最终要通过查看SubList类中的代码才能理解subList方法的具体情况。(在此暂不讨论RandomAccess)

SubList类的构造方法及该类中的变量:

private AbstractList<E> l;
private int offset;
private int size;
private int expectedModCount;

SubList(AbstractList<E> list, int fromIndex, int toIndex) {
if (fromIndex < 0)
throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
if (toIndex > list.size())
throw new IndexOutOfBoundsException("toIndex = " + toIndex);
if (fromIndex > toIndex)
throw new IllegalArgumentException("fromIndex(" + fromIndex +
") > toIndex(" + toIndex + ")");
l = list;
offset = fromIndex;
size = toIndex - fromIndex;
expectedModCount = l.modCount;
}


在做出一系列合法性判断后,只是做了简单的赋值。此处的 l 即为原始的list, offset是fromIndex, size是toIndex - fromIndex,即总长度, expectedModCount初值为l.modCount

在之前代码中:

List<Integer> sub = list.subList(0, 2);
sub.add(5);


执行这两句代码后,sub执行的其实是SubList中的add方法,那么这个add方法做了什么,如下:

public void add(int index, E element) {
if (index<0 || index>size)
throw new IndexOutOfBoundsException();
checkForComodification();
l.add(index+offset, element);
expectedModCount = l.modCount;
size++;
modCount++;
}


在这个add方法中,首先调用了 l 的add方法,随即修改了size的值,也就是说,SubList只是对原来的List进行了管理,而并不是单独的List

看看它的get方法:

public E get(int index) {
rangeCheck(index);
checkForComodification();
return l.get(index+offset);
}


return的结果是 l 的 index+offset位置的元素值

综上,subList方法获得的List并不是截取出的一个List,而是通过对原来的List做了封装,提供了一些方法来管理,通过偏移量来进行控制,因此一旦改变subList,原来的List同时发生变动。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: