1 手寫ArrayList核心源碼
手寫ArrayList核心源碼
ArrayList是Java中常用的數據結構,不光有ArrayList,還有LinkedList,HashMap,LinkedHashMap,HashSet,Queue,PriorityQueue等等,我們將手寫這些常用的數據結構的核心源碼,用盡量少的代碼來揭示核心原理。
下面我們來手寫ArrayList的核心源碼
首先我們定義一個QArrayList,不要問為什麽叫QArrayList,因為我之前寫過Qt,僅此而已。源碼 public class<T> QArrayList
,Java中的ArrayList的底層就是用一個Object[] 結構來保存數據的。我們也要定義一個Object[] 屬性。
而且我們還要定義一個默認的數據的大小,以便在調用默認構造函數的情況下使用。
private final int DEFAULT_LIST_SIZE = 8;
還要定義一個 int mSize
變量,mSize 默認為0
,代表下一個可以存放數據的數組的索引,代表下一個可以存放數據的數組的索引,代表下一個可以存放數據的數組的索引
重要的事情說三遍
到現在為止我們的類如下:
public class QList<T> { //默認的數組的大小 private final int DEFAULT_LIST_SIZE = 8; //存放數據的地方 private Object[] mData; //下一個可以存放數據的當前數組的索引 private int mSize; ...... }
好了,存放數據的數組也有了,下一個可以存放數據的當前的數組的索引也有了
ArrayList 底層是用數組存放數據,那麽會有一個問題,如果此時數組滿了我們再往裏面存放數據的時候,怎麽辦呢?ArrayList是再新建一個數組,新數組的大小是原來數組大小的2倍,那麽我們也這樣做。
此時,我們實現 add,get,remove,resize等這幾個核心方法,QArrayList完整的代碼如下 :
public class QArrayList<T> { //默認的數組的大小 private final int DEFAULT_LIST_SIZE = 8; //存放數據的地方 private Object[] mData; //下一個可以存放數據的當前數組的索引 private int mSize; public QArrayList() { //new 一個數組,用來存放 mData = new Object[DEFAULT_LIST_SIZE]; //下一個可以存放數據的當前數組的索引為0 mSize = 0; } public QArrayList(int capacity){ if(capacity <= 0 || capacity > Integer.MAX_VALUE){ throw new RuntimeException("invalid capacity"); } mData = new Object[capacity]; mSize = 0; } //返回當時數組的已經存放了多少個元素 public int size() { return mSize; } //返回數組的總大小,其實這個接口沒有必要對外提供,這裏我們只是為了演示用 public int capacity() { return mData.length; } //添加一個元素 public void add(T e) { //規定不允許添加一個空元素 if(e == null){ return; } //如果當前數組已經滿了,擴容為原來數組的2倍 if (mSize >= mData.length) { //擴容 resize(); } //將添加的元素添加到數組中 mData[mSize] = e; //同時 mSize++ 指向下一個可以存放數據的位置 mSize++; } //獲取指定位置的元素,如果position不合法,直接拋出異常 //這樣做是有必要的,我們提供的是一個庫 // 直接拋出異常讓使用知道用錯了,沒有必要 return null // 因為這是個庫,不是業務,就算return null,也是業務層的事 public T get(int position) { if (position < 0 || position >= mData.length) { throw new RuntimeException("position is invalid"); } // position 大於 mSize 也沒有關系,因為也是返回null,證明沒有獲取到 return (T) mData[position]; } //刪除指定位置的元素 public T remove(int position) { //和上面一樣,位置不合法直接拋出異常 if (position < 0 || position >= mData.length) { throw new RuntimeException("position is invalid"); } //把當前要刪除的元素保存下來,最後返回要刪除的元素 T e = (T) mData[position]; //刪除後,把後面的所有元素都往前移位 for (int i = position + 1; i < mData.length; i++) { mData[i - 1] = mData[i]; } //別忘了 mSize 要 -- mSize--; //返回刪除的元素 return e; } //刪除指定的元素 public boolean remove(T e) { //因為數組可能沒有滿,如果刪除的是null,沒有必要,我們不允許 if (e == null) { return false; } //找到刪除元素的位置 int position = -1; for (int i = 0; i < mData.length; i++) { if (e == mData[i] || e.equals(mData[i])) { position = i; break; } } //沒有找到就返回 if (position == -1) { return false; } //刪除 return remove(position) != null; } //擴容,我們都以2倍的容量擴容 private void resize() { Object[] old = mData; mData = new Object[mData.length * 2]; for (int i = 0; i < old.length; i++) { mData[i] = old[i]; } old = null; } }
註釋都有相關的解釋
我們來測試,測試代碼如下:
public static void main(String[] args) {
QArrayList<String> list = new QArrayList<>();
list.add("tom");
list.add("jim");
list.add("lilei");
list.add("hanmeimei");
System.out.println("list.get(2)=" + list.get(2));
System.out.println("list.size()=" + list.size());
for (int i = 0; i < list.size(); i++) {
System.out.println("list.get(" + i + ") = " + list.get(i));
}
System.out.println("=======================");
System.out.println("演示刪除操作");
list.remove("jim");
for (int i = 0; i < list.size(); i++) {
System.out.println("list.get(" + i + ") = " + list.get(i));
}
}
輸出如下:
list.get(2)=lilei
list.size()=4
list.get(0) = tom
list.get(1) = jim
list.get(2) = lilei
list.get(3) = hanmeimei
============
演示刪除操作
list.get(0) = tom
list.get(1) = lilei
list.get(2) = hanmeimei
但是最重要的擴容功能還沒有演示,下面是擴容演示的測試代碼:
public static void main(String[] args) {
//新建一個只有2個元素的數組
QArrayList<String> list = new QArrayList<>(2);
//打印出擴容後的容量
System.out.println("擴容前 : list.capacity()=" + list.capacity());
//我們添加了4個元素
list.add("tom");
list.add("jim");
list.add("lilei");
list.add("hanmeimei");
//打印出擴容後的容量
System.out.println("擴容後 : list.capacity()=" + list.capacity());
//打印
for (int i = 0; i < list.size(); i++) {
System.out.println("list.get(" + i + ") = " + list.get(i));
}
}
輸出如下:
擴容前 : list.capacity()=2
擴容後 : list.capacity()=4
list.get(0) = tom
list.get(1) = jim
list.get(2) = lilei
list.get(3) = hanmeimei
可以看到,我們新建了一個底層只有2個元素的數組,但是我們添加了4個元素,我們打印出擴容後的數組的容量是 4 ,可見我們的擴容機制是沒有問題的。
以上就是QArrayList的核心原理,我們下節手寫LinkedList的核心原理
1 手寫ArrayList核心源碼