1. 程式人生 > >1 手寫ArrayList核心源碼

1 手寫ArrayList核心源碼

演示 構造函數 打印 滿了 hashset 當前 大小 move 保存

手寫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核心源碼