手寫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的核心原理
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1343/viewspace-2817917/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 手寫 ArrayList 核心原始碼原始碼
- 面試必會之ArrayList原始碼分析以及手寫ArrayList面試原始碼
- 手寫 Java HashMap 核心原始碼JavaHashMap原始碼
- 純手寫Arraylist集合框架框架
- ArrayList 原始碼分析原始碼
- [原始碼分析]ArrayList原始碼
- ArrayList原始碼分析原始碼
- ArrayList原始碼解析原始碼
- 手寫Vuex原始碼Vue原始碼
- 集合-ArrayList 原始碼解析原始碼
- ArrayList方法原始碼分析原始碼
- ArrayList-原始碼分析原始碼
- ArrayList原始碼(改)(查)原始碼
- Java——ArrayList原始碼解析Java原始碼
- 1、ArrayList原始碼解析原始碼
- 搞懂 Java ArrayList 原始碼Java原始碼
- 原始碼分析之ArrayList原始碼
- 手寫@koa/router原始碼原始碼
- 集合原始碼分析[3]-ArrayList 原始碼分析原始碼
- Java集合原始碼剖析——ArrayList原始碼剖析Java原始碼
- 【Java集合原始碼剖析】ArrayList原始碼剖析Java原始碼
- 竟然還有人說ArrayList是2倍擴容,今天帶你手撕ArrayList原始碼原始碼
- 手寫Koa.js原始碼JS原始碼
- 手寫Express.js原始碼ExpressJS原始碼
- ArrayList詳解-原始碼分析原始碼
- 【Java集合】ArrayList原始碼分析Java原始碼
- ArrayList原始碼閱讀(增)原始碼
- ArrayList & LinkedList原始碼解析原始碼
- JAVA集合:ArrayList原始碼分析Java原始碼
- jdk原始碼分析之ArrayListJDK原始碼
- Java容器原始碼學習--ArrayList原始碼分析Java原始碼
- 求職之手寫程式碼-手寫原始碼大雜燴求職原始碼
- 【原始碼解析】- ArrayList原始碼解析,絕對詳細原始碼
- 手寫Redux-Saga原始碼Redux原始碼
- ArrayList原始碼閱讀筆記原始碼筆記
- ArrayList原始碼分析(JDK1.8)原始碼JDK
- Java集合之ArrayList原始碼解析Java原始碼
- 原始碼分析–ArrayList(JDK1.8)原始碼JDK