來自mrcode:代碼有毒
從結(jié)構(gòu)來看
4個頂層接口來看至少有以下功能
1.Cloneable:
此類實現(xiàn)了 Cloneable 接口,以指示 Object.clone() 方法可以合法地對該類實例進(jìn)行按字段復(fù)制。
2.RandomAccess:
List 實現(xiàn)所使用的標(biāo)記接口,用來表明其支持快速(通常是固定時間)隨機訪問。此接口的主要目的是允許一般的算法更改其行為,從而在將其應(yīng)用到隨機或連續(xù)訪問列表時能提供良好的性能。
3.Serializable:
序列化接口沒有方法或字段,僅用于標(biāo)識可序列化的語義
4.Iterable:
實現(xiàn)這個接口允許對象成為 “foreach” 語句的目標(biāo)。
實現(xiàn)/繼承
1.AbstractList:
此類提供 List 接口的骨干實現(xiàn),以最大限度地減少實現(xiàn)“隨機訪問”數(shù)據(jù)存儲(如數(shù)組)支持的該接口所需的工作。對于連續(xù)的訪問數(shù)據(jù)(如鏈表),應(yīng)優(yōu)先使用 AbstractSequentialList,而不是此類。
2.List:
有序的 collection(也稱為序列)。此接口的用戶可以對列表中每個元素的插入位置進(jìn)行精確地控制。用戶可以根據(jù)元素的整數(shù)索引(在列表中的位置)訪問元素,并搜索列表中的元素。
3.AbstractCollection:
此類提供 Collection 接口的骨干實現(xiàn),以最大限度地減少了實現(xiàn)此接口所需的工作。
4.Collection:Collection
層次結(jié)構(gòu) 中的根接口。Collection 表示一組對象,這些對象也稱為 collection 的元素。一些 collection 允許有重復(fù)的元素,而另一些則不允許。一些 collection 是有序的,而另一些則是無序的。JDK 不提供此接口的任何直接 實現(xiàn):它提供更具體的子接口(如 Set 和 List)實現(xiàn)。此接口通常用來傳遞 collection,并在需要最大普遍性的地方操作這些 collection。
從功能閱讀-簡單函數(shù)開始
一般在使用中最常用的就是,數(shù)據(jù)的存、取、刪、循環(huán)遍歷,就從這幾個點分析源碼。
要使用就得先初始化對象實例。
構(gòu)造函數(shù)
/**
* Constructs an empty list with an initial capacity of ten.
* 構(gòu)建一個初始化容量為10的空列表
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 用指定容量創(chuàng)建列表
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
this.elementData = new Object[initialCapacity];
}
add - 將指定的元素添加到此列表的尾部。
//把元素添加到元素末尾
public boolean add(E e) {
//確保列表的容量足夠
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
add - 將指定的元素插入此列表中的指定位置。
總結(jié):
- 下標(biāo)不能越界
- 每次操作都需要移動元素的個數(shù):頻繁使用該方法將會影響性能
// 將指定的元素插入次列表中的指定位置
//將指定位置元素開始的下標(biāo)都往后+1(往后移動一個位置)
public void add(int index, E element) {
//檢查指定下標(biāo)是否越界
rangeCheckForAdd(index);
//調(diào)用核心函數(shù)1 確保足夠的容量
ensureCapacityInternal(size + 1); // Increments modCount!!/
// 移動元素
System.arraycopy(elementData, index, elementData, index + 1, size - index);
elementData[index] = element;
size++;
}
/**
* A version of rangeCheck used by add and addAll.
* 檢查指定下標(biāo)是否越界,該方法供add 和 addAll使用
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
核心函數(shù)1:確保足夠的列表容量
總結(jié):
- 支持最大容量:Integer.MAX_VALUE(約21億條數(shù)據(jù))
- 每次擴容都在原有基礎(chǔ)上增加一半的容量;所以適當(dāng)?shù)墓浪愠跏既萘渴呛苤匾模J(rèn)初始容量 10)
//確保列表的容量足夠:1. 入口
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
//1.1. 修改次數(shù)+1,是否擴容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument. *
* @param minCapacity the desired minimum capacity
* 擴容;至少能容納下minCapacity所需要的容量
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 新的容量= 以前容量 + 以前容量的一半
int newCapacity = oldCapacity + (oldCapacity >> 1);
//確保計算出來的新容量至少能裝得下所需要的容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新的容量 比 內(nèi)置的最大容量限制還要大
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 進(jìn)行重新計算確定新的容量:結(jié)果:最大Integer.MAX_VALUE
// 所以這里的結(jié)果就是:一個list最多支持21億多條數(shù)據(jù),一般也不會用到這么大的,完全夠用
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 把原來的數(shù)據(jù)拷貝到新容量數(shù)組中去
elementData = Arrays.copyOf(elementData, newCapacity);
}
remove - 按索引移除元素
總結(jié):
- 移除索引不能大于最大元素數(shù)量
- 每次操作都需要對移除后的數(shù)據(jù)進(jìn)行移動操作:在刪除較多的場景里面使用該方法影響性能
//按索引移除該元素,并返回被移除的元素值
public E remove(int index) {
// 檢查索引的有效性,索引大于size則數(shù)組越界
rangeCheck(index);
modCount++; //快速失敗思想,增加修改次數(shù)
E oldValue = elementData(index);
//計算需要移動的元素個數(shù)
int numMoved = size - index - 1;
if (numMoved > 0)
//把源數(shù)組的刪除索引之后的元素都往前copy覆蓋
System.arraycopy(elementData, index+1, elementData, index,numMoved);
//置空最后一個元素,并把size減少1
//這里置空元素,只是為了讓gc盡快的回收掉移除的元素,小技巧用得很對
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
// 檢查指定下標(biāo)是否在范圍內(nèi)
private void rangeCheck(int index) {
//這里可以看到只檢查了是否大于等于siez,如果為負(fù)數(shù)的話,則沒有檢查,那么在插入的時候Native Method會拋出數(shù)據(jù)越界的
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
remove - 移除指定的元素
總結(jié):
- 按value刪除元素,只會刪除第一次出現(xiàn)的元素
- 按value刪除元素,在內(nèi)部會進(jìn)行for循環(huán)匹配元素,并且要移動受影響的元素:所以對于刪除來說,list性能稍低
// 移除指定的元素,如果這個元素存在的話,并且移除的是第一次出現(xiàn)的元素
// 移除成功或則返回true,未搜索找元素則返回false
public boolean remove(Object o) {
//因為list支持 null 元素,所以對null進(jìn)行特殊處理
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
//使用元素下標(biāo)進(jìn)行遍歷判斷
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
/*
* Private remove method that skips bounds checking and does not
* return the value removed.
* 私有的刪除元素方法,不進(jìn)行邊界檢查和是否存在的檢查,直接按照指定的索引進(jìn)行刪除
* (貌似又學(xué)到一個規(guī)則:公開的的方法必須校驗邊界規(guī)則什么的,私有的方法就不用了,方便公用 * 嗎?)
* 該方法的代碼和public remove(index) 中的代碼部分主要刪除邏輯一致
*/
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,numMoved);
elementData[--size] = null; // clear to let GC do its work
}
set - 用指定的元素替代此列表中指定位置上的元素。
總結(jié):
- 直接根據(jù)下標(biāo)替換掉原有元素
//用指定的元素替代此列表中指定位置上的元素。
public E set(int index, E element) {
//在remove中也使用了該函數(shù),檢查 index>=siez
rangeCheck(index);
//記錄替換之前的元素
E oldValue = elementData(index);
//直接更新
elementData[index] = element;
return oldValue;
}
//獲得指定下標(biāo)的 元素
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
get - 返回此列表中指定位置上的元素。
public E get(int index) {
//檢查下標(biāo)是否越界size
rangeCheck(index);
//直接通過下標(biāo)拿到了元素
return elementData(index);
}
Iterator - 遍歷
總結(jié)一
要讓一個對象擁有foreach的目標(biāo),則需要有以下兩個要求:
??1.目標(biāo)對象實現(xiàn) Iterable 接口
??2.實現(xiàn)Iterator,對數(shù)據(jù)進(jìn)行常用操作訪問實現(xiàn)接口的方法會返回一個 Iterator 類型的對象,所以得自己實現(xiàn)對自己數(shù)據(jù)結(jié)構(gòu)的訪問等操作
foreach 是調(diào)用了 Iterator 的方法實現(xiàn)的。
所以問題就來了:
for(i=0;i<size;i++) 這種語法是不是和迭代器無關(guān)了,從這個看來,for(;;)只是對i進(jìn)行了
一個自增的操作,和具體的對象不是綁定狀態(tài),但是由于ArrayList能使用下標(biāo)訪問數(shù)據(jù),所以
就能多一種遍歷方式了
總結(jié)二
1.遍歷獲取元素 是通過下標(biāo)直接獲取
??2.但是刪除元素的話是委托了 原來的刪除方式,很原來的刪除原理一致,但是保證了當(dāng)前元素下標(biāo)的正確性,彌補了for(;;)刪除導(dǎo)致容器size變化和元素下標(biāo)位置發(fā)生變化 從而無法獲取正確的元素的 問題
??3.Iterator 可以使用while來遍歷
// 返回一個在一組 T 類型的元素上進(jìn)行迭代的迭代器。
// 這個跌迭代器是 快速失敗的,也就是會檢查modCount,發(fā)現(xiàn)不對就拋出異常
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
* 對父類的優(yōu)化。- 這個抽象層次真好
*/
private class Itr implements Iterator<E> {
// 返回下一步元素的索引
int cursor; // index of next element to return
//返回最后一次操作的元素索引,-1 表示沒有操作過元素,用于刪除操作使用
int lastRet = -1; // index of last element returned; -1 if no such
//快速失敗檢查,保留一個當(dāng)前迭代器所持有的數(shù)據(jù)版本;
int expectedModCount = modCount;
//是否有下一個元素
public boolean hasNext() {
// 當(dāng)前游標(biāo)不等于size的話,就表示還有下一個元素
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor; //在開頭獲取下一步的值,且以0開始,則i表示當(dāng)前操作的元素下標(biāo)
if (i >= size) /當(dāng)前元素索引如果大于了size
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
// 判斷 當(dāng)前下標(biāo)是否大于了數(shù)組的長度(什么情況下會出現(xiàn)此問題呢?)
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1; //記錄下一步要操作的元素
return (E) elementData[lastRet = i];
}
public void remove() {
// 如果還沒有操作過元素,比如,沒有調(diào)用next方法,就調(diào)用該方法就表示狀態(tài)不正確
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification(); //每次操作都需要檢查是否快速失敗
try {
//調(diào)用原來的移除方法進(jìn)行刪除元素,當(dāng)前也會使modCount次數(shù)增加
ArrayList.this.remove(lastRet);
cursor = lastRet; //因為刪除了元素,刪除下標(biāo)后面的元素都會往前移動
lastRet = -1; //變成-1 狀態(tài),如果連續(xù)調(diào)用該方法則拋出異常(這里設(shè)計得真的好巧妙,保證了刪除操作)
//更改當(dāng)前迭代器所持有的數(shù)據(jù)版本,否則就會導(dǎo)致快速失敗異常了
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
//對于數(shù)據(jù)越界操作,都定義為 當(dāng)方法檢測到對象的并發(fā)修改,但不允許這種修改時,拋出此異常
throw new ConcurrentModificationException();
}
}
//檢查當(dāng)前迭代器的版本是否與 容器列表中的數(shù)據(jù)版本是否一致,如果不一致,那么當(dāng)前迭代數(shù)據(jù)就會發(fā)生下標(biāo)越界等異常,所以需要拋出異常(比如在多線程中,或則在迭代中使用list.remove() 或則add等方法 都會導(dǎo)致拋出異常)
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
contains - 如果包含指定的元素返回true
總結(jié):
??1.查找是否包含同樣是從頭到尾的循環(huán)遍歷匹配
??2.是否匹配 使用equals方法比對,所以自己可以覆蓋equals方法來查找引用型自定義對象
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
// 在數(shù)組中循環(huán)遍歷查找指定的元素,如果存在則返回該元素下標(biāo),是返回首次出現(xiàn)的元素哦
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
//使用的是equals方法
if (o.equals(elementData[i]))
return i;
}
return -1;
}
小結(jié)總結(jié)
其實從上面已經(jīng)就可以看出來了,list的一些常用總結(jié)特性:底層使用數(shù)組實現(xiàn)
優(yōu)點:
??1.隨機訪問比較快,因為直接通過下標(biāo)獲取元素
??2.覆蓋更新元素也比較快,也是直接通過下標(biāo)覆蓋
缺點
??1.在remove多的場景下性能稍微低下(針對其他的集合來說)
??2.在按value remove的情況下會循環(huán)遍歷整個數(shù)組,性能稍低
??3.隨機插入操作性能較低,因為需要把插入下標(biāo)后面所有的元素都移動位置
??4.當(dāng)前容量不夠的時候會觸發(fā)擴容操作,每次擴容都需要把源數(shù)據(jù)拷貝到新的擴容數(shù)組中去,不合理估算初始容量的話,性能較低
源碼分析的測試用列:
因為是工具類,直接對使用的方法進(jìn)行分析;
public class ArrayListStudyTest {
private ArrayList<Integer> data;
@Before
public void createTestData(){
data = new ArrayList<>(3);
data.add(0);
data.add(1);
data.add(2);
}
@Test
public void add(){
data.add(1,2);
}
@Test
public void remove(){
data.remove(1);
data.remove(Integer.valueOf(2));
}
@Test
public void set(){
data.set(-1,3);
}
@Test
public void get(){
Integer integer = data.get(0);
}
@Test
public void foreach(){
Iterator<Integer> iterator = data.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
}
for (Integer num : data) {
System.out.println(num);
}
}
}
編寫自己的Arraylist
簡單編寫了下 基本功能的arraylist,編寫過程中發(fā)現(xiàn),要費不少時間,特別是你還要考慮到各種暴露方法調(diào)用會出現(xiàn)的bug,現(xiàn)在才感覺一個工具類真的可能要經(jīng)過多次迭代和認(rèn)真考慮各個使用環(huán)境下,才能編寫出好的工具類
/**
* @author zhuqiang
* @version V1.0
* @date 2016/7/22 0022 9:44
*/
public class MyArrayList<E> implements Iterable<E>{
private Object[] elementData; //底層實現(xiàn)用數(shù)組
private final static Object[] EMPTY_ELEMENTDATA = {};
private final static int DEFAULT_CAPACITY = 10; //默認(rèn)初始容量
private int size; //容器大小
private int modCount; //快速失敗 版本
public MyArrayList() {
elementData = EMPTY_ELEMENTDATA;
}
public MyArrayList(int initialCapacity) {
if(initialCapacity < 0){
throw new IllegalArgumentException("初始化容量不能小于0");
}
this.elementData = new Object[initialCapacity];
}
/**
* 把元素添加到列表末尾
* @param e
* @return
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
// 確保底層容量能存儲數(shù)據(jù)
private void ensureCapacityInternal(int minCapacity) {
if(EMPTY_ELEMENTDATA == elementData){
this.elementData = new Object[DEFAULT_CAPACITY];
}
if(minCapacity - this.elementData.length > 0){
ensureCapacity(minCapacity);
}
}
// 擴容操作
private void ensureCapacity(int minCapacity) {
int oldCapacity = this.elementData.length;
int newCapacity = oldCapacity + (oldCapacity>>1); //新的容量計算
elementData = Arrays.copyOf(elementData,newCapacity); //把原數(shù)據(jù)拷貝到新的容量數(shù)組中
}
/**
* 移除指定下標(biāo)的元素
* @param index
* @return
*/
public E remove(int index){
E oldValue = elementData(index);
//計算需要移動的元素個數(shù)
int numMoved = size - index -1; // 減掉被移除的自己
System.arraycopy(elementData,index+1,elementData,index,numMoved);
elementData[--size] = null;
return oldValue;
}
/**
* 替換指定下標(biāo)的元素
* @param index
* @param element 返回被替換掉的元素的值
* @return
*/
public E set(int index, E element){
E oldValue = elementData(index);
this.elementData[index] = element;
return oldValue;
}
public E get(int index){
return elementData(index);
}
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
public int size(){
return size;
}
@Override
public Iterator iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
@Override
public boolean hasNext() {
return cursor != size;
}
@Override
public E next() {
int i = cursor;
E e = MyArrayList.this.elementData(i);
cursor++;
return e;
}
@Override
public void remove() {
}
}
public static void main(String[] args) {
MyArrayList<Integer> data = new MyArrayList<>();
data.add(0);
data.add(1);
data.add(2);
data.add(3);
for (Integer i : data) {
System.out.println(i);
}
System.out.println("==========");
for (int i = 0; i < data.size(); i++) {
System.out.println(data.get(i));
}
System.out.println("==========");
data.remove(1);
for (int i = 0; i < data.size(); i++) {
System.out.println(data.get(i));
}
}
}