導語
在日常的開發中通常會遇到截取List的情況,而大多數會選擇使用subList方法進行截取,但是好多人對這個方法的理解都只是停留在使用層面上?這篇文章會非常詳細達到源碼級別的講解sublList方法,需要的朋友趕緊收藏起來吧。
關於SubList
先通過下面這個例子,看看具體的返回類型:
public class TestSubList {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(""+i);
}
List<String> subList = list.subList(3, 6);
System.out.println(subList.getClass()+" "+subList);
System.out.println(list.getClass()+" "+list);
}
}
輸出結果:
class java.util.ArrayList$SubList [3, 4, 5]
class java.util.ArrayList [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
通過例子可以看出,當對list進行subList之後返回的subList對象,其實是一個內部類SubList,嚴格意思上來說,subList是ArrayList對象的一個視圖,對於subList對象的操作都會映射到原來的ArrayList集合中。再通過下面這個例子看下具體的操作影響。
public class TestSubList {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(""+i);
}
List<String> subList = list.subList(3, 6);
System.out.println(subList.getClass()+" "+subList);
System.out.println(list.getClass()+" "+list);
System.out.println("----------------");
subList.add("subList添加");
System.out.println(subList.getClass()+" "+subList);
System.out.println(list.getClass()+" "+list);
}
}
輸出結果:
class java.util.ArrayList$SubList [3, 4, 5]
class java.util.ArrayList [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
----------------
class java.util.ArrayList$SubList [3, 4, 5, subList添加]
class java.util.ArrayList [0, 1, 2, 3, 4, 5, subList添加, 6, 7, 8, 9]
從上面可以看出,對subList的操作已經影響到ArrayList了,下面咱根據源碼進行詳細的分析一下原因。
關於集合類,《阿里巴巴Java開發手冊》中其實還有另外一個規定:
源碼概覽
// subList方法
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
從源碼中可以看出,其實subList方法返回的是ArrayList的內部類SubList,並不是ArrayList,所以,在使用的時候會有許多的注意細節。
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> 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;
}
public E set(int index, E e) {
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
public int size() {
checkForComodification();
return this.size;
}
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
this.modCount = parent.modCount;
this.size -= toIndex - fromIndex;
}
public boolean addAll(Collection<? extends E> c) {
return addAll(this.size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
int cSize = c.size();
if (cSize==0)
return false;
checkForComodification();
parent.addAll(parentOffset + index, c);
this.modCount = parent.modCount;
this.size += cSize;
return true;
}
}
其實從SubList類的源碼中可以看出,SubList類中也實現了和ArrayList中的一樣的方法,所以在調用subList的一些方法時,運行的是SubList中的實現,而且從上面可以看出,真正操作的還是原ArrayList對象。
源碼詳解
先看subList方法以及SubList的構造方法:
// subList方法
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
// SubList構造方法
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
// 將截取長度作爲SubList的長度
this.size = toIndex - fromIndex;
// 將ArrayList對象的modCount賦值給SubList對象
this.modCount = ArrayList.this.modCount;
}
從上面可以看出,當構造SubList對象的時候,會存儲原集合在parent變量中,並且把截取的開始下標存爲parentOffset。
下面詳細看一個SubList的add方法,對上面的現象進行一個詳細的分析。
// SubList對象的add方法
public void add(int index, E e) {
// 檢查下標是否合規
rangeCheckForAdd(index);
// 檢查modCount是否和ArrayList的modCount一致(注:modCount表示的是集合的結構變化次數)
checkForComodification();
// 在原ArrayList指定位置添加元素
parent.add(parentOffset + index, e);
// 將ArrayList對象的modCount賦值給SubList對象
this.modCount = parent.modCount;
// 使SubList對象的長度加一
this.size++;
}
通過上面的方法講解,可以看出,對SubList對象的操作,其實就是對ArrayList對象的操作,其他的方法也都是同理。
總結
本篇文章詳細介紹了ArrayList的subLIst方法以及SubList類的用法,由於純手打,難免會有紕漏,如果發現錯誤的地方,請第一時間告訴我,這將是我進步的一個很重要的環節。