List接口提供了subList方法,其作用是返回一個列表的子列表,這與String類的subString有點類似,但它們的功能是否相同呢?我們來看如下代碼:
public static void main(Stringargs){
//定義一個包含兩個字符串的列表
List<String>c=new ArrayList<String>();
c.add(\"A\");
c.add(\"B\");
//構造一個包含c列表的字符串列表
List<String>c1=new ArrayList<String>(c);
//subList生成與c相同的列表
List<String>c2=c.subList(0,c.size());
//c2增加一個元素
c2.add(\"C\");
System.out.println(\"c==c1?\"+c.equals(c1));
System.out.println(\"c==c2?\"+c.equals(c2));
}
c1是通過ArrayList的構造函數創建的,c2是通過列表的subList方法創建的,然後c2又增加了一個元素C,現在的問題是輸出的結果是什麼呢?列表c與c1、c2之間是什麼關係呢?
別忙著回答這個問題,我們先來回想一下String類的subString方法,看看它是如何工作的,代碼如下:
public static void main(Stringargs){
String str=\"AB\";
String str1=new String(str);
String str2=str.substring(0)+\"C\";
System.out.println(\"str==str1?\"+str1.equals(str1));
System.out.println(\"str==str2?\"+str.equals(str2));
}
很明顯,str與str1是相等的(雖然不是同一個對象,但用equals方法判斷是相等的),但它們與str2不相等,這毋庸置疑,因為str2在對像池中重新生成了一個新的對象,其表面值是ABC,那當然與str和str1不相等了。
說完了subString的小插曲,現在回到List是否相等的判斷上來。subList與subString的輸出結果是一樣的嗎?讓事實說話,運行結果如下:
c==c1?false
c==c2?true
很遺憾,與String類剛好相反,同樣是一個sub類型的操作,為什麼會相反呢?僅僅回答「為什麼」似不足以平復我們的驚訝,下面就從最底層的源代碼來進行分析。
c2是通過subList方法從c列表中生成的一個子列表,然後c2又增加了一個元素,可為什麼增加了一個元素還會相等呢?我們來看subList源碼:
public List<E>subList(int fromIndex, int toIndex){
return(this instanceof RandomAccess?
new RandomAccessSubList<E>(this, fromIndex, toIndex):
new SubList<E>(this, fromIndex, toIndex));
}
subList方法是由AbstractList實現的,它會根據是不是可以隨機存取來提供不同的SubList實現方式,不過,隨機存儲的使用頻率比較高,而且RandomAccessSubList也是SubList子類,所以所有的操作都是由SubList類實現的(除了自身的SubList方法外),那麼,我們就直接來看SubList類的代碼:
class SubList<E>extends AbstractList<E>{
//原始列表
private AbstractList<E>l;
//偏移量
private int offset;
//構造函數,注意list參數就是我們的原始列表
SubList(AbstractList<E>list, int fromIndex, int toIndex){
/*下標校驗,省略*/
//傳遞原始列表
l=list;
offset=fromIndex;
//子列表的長度
size=toIndex-fromIndex;
}
//獲得指定位置的元素
public E get(int index){
/*校驗部分,省略*/
//從原始字符串中獲得指定位置的元素
return l.get(index+offset);
}
//增加或插入
public void add(int index, E element){
/*校驗部分,省略*/
//直接增加到原始字符串上
l.add(index+offset, element);
/*處理長度和修改計數器*/
}
/*其他方法省略*/
}
通過閱讀這段代碼,我們就非常清楚subList方法的實現原理了:它返回的SubList類也是AbstractList的子類,其所有的方法如get、set、add、remove等都是在原始列表上的操作,它自身並沒有生成一個數組或是鏈表,也就是子列表只是原列表的一個視圖(View),所有的修改動作都反映在了原列表上。
我們例子中的c2增加了一個元素C,不過增加的元素C到了c列表上,兩個變量的元素仍保持完全一致,相等也就很自然了。
解釋完相等的問題,再回過頭來看看為什麼變量c與c1不相等。很簡單,因為通過ArrayList構造函數創建的List對像c1實際上是新列表,它是通過數組的copyOf動作生成的,所生成的列表c1與原列表c之間沒有任何關係(雖然是淺拷貝,但元素類型是String,也就是說元素是深拷貝的),然後c又增加了元素,因為c1與c之間已經沒有一毛錢的關係了,那自然是不相等了。
注意 subList產生的列表只是一個視圖,所有的修改動作直接作用於原列表。