List ADT實(shí)現(xiàn)之增長數(shù)組實(shí)現(xiàn)(ArrayList)

List ADT有兩種流行的實(shí)現(xiàn)方式,Java中ArrayList類提供了可增長數(shù)組的實(shí)現(xiàn)穆碎,LinkedList提供了雙鏈表實(shí)現(xiàn)。

使用ArrayList的有點(diǎn)在于,對get和set操作花費(fèi)常數(shù)時(shí)間橄仍。其缺點(diǎn)是新元素的插入和元素的刪除付出的代價(jià)非常昂貴,除非變動(dòng)實(shí)在末端進(jìn)行牍戚。

為說明增長數(shù)組實(shí)現(xiàn)的一些思想侮繁,本篇文章將編寫ArrayList的簡單實(shí)現(xiàn)。為避免與類庫中的ArrayList類相混淆如孝,這里將把實(shí)現(xiàn)的類叫做MyArrayList宪哩,即MyArrayList是獨(dú)立的。

MyArrayList的一些細(xì)節(jié):

  1. 維護(hù)數(shù)組的元素(elementData)第晰,數(shù)組的容量(elementData.length)锁孟,數(shù)組當(dāng)前的存儲(chǔ)的元素個(gè)數(shù)(size)彬祖。
  2. 提供一種機(jī)制來動(dòng)態(tài)改變數(shù)組的容量。通過建立新的數(shù)組品抽,將老數(shù)組的元素拷貝到新數(shù)組中储笑,然后允許虛擬機(jī)回收老數(shù)組。
  3. 提供set和get操作的實(shí)現(xiàn)圆恤。
  4. 提供基本操作突倍,例如:size(),isEmpty()盆昙,和Clear()羽历。提供remove(...)操作和add(...)操作 。
  5. MyArrayList實(shí)現(xiàn)Iterable接口淡喜,并提供了一個(gè)實(shí)現(xiàn)Iterator接口的實(shí)現(xiàn)類MyListIterator秕磷,該類實(shí)現(xiàn)了hasNext(),next()和remove()操作拆火。最后通過實(shí)現(xiàn)的iterator放回一個(gè)Iterator實(shí)例跳夭。

代碼####

public class MyArrayList<E> implements Iterable<E> {

    private static final int DEFULT_CAPASITY = 10;

    private int size; // 維護(hù)數(shù)組當(dāng)前元素的個(gè)數(shù)
    private E[] elementData; // 存儲(chǔ)當(dāng)前List的元素

    // 構(gòu)造器
    public MyArrayList() {
        doClear();
    }

    // 清空List
    public void clear() {
        doClear();
    }

    // 清空List操作,設(shè)置size為0们镜,elementData容量為默認(rèn)值10
    private void doClear() {
        size = 0;
        ensureCapacity(DEFULT_CAPASITY);
    }

    // 獲取size
    public int size() {
        return size;
    }

    // 判斷是否為空
    public boolean isEmpty() {
        return size == 0;
    }

    // 為了避免浪費(fèi)空間币叹,設(shè)置容量為size
    public void trimToSize() {
        ensureCapacity(size);
    }

    // 通過索引獲取元素
    public E get(int index) {
        CheckRange(index);
        return elementData[index];
    }

    // 修改某個(gè)位子的元素
    public E set(int index, E element) {
        CheckRange(index);
        E old = elementData[index];
        elementData[index] = element;
        return old;
    }

    // 保證對List的操作時(shí)有足夠的空間。比如add操作模狭,如果數(shù)組的容量和size相同颈抚,則沒有位子可以添加元素。
    // 傳入一個(gè)新的容量minCapacity嚼鹉,表示需要的最小容量贩汉。
    // 如果size>=newCapacity,則說明容量足夠锚赤,不作處理匹舞。
    // 如果size<newCapacity,則將elementData擴(kuò)容至minCapacity线脚。
    @SuppressWarnings("unchecked")
    public void ensureCapacity(int minCapacity) {
        if (size >= minCapacity) {
            return;
        }

        E[] old = elementData; // 將elementData拷貝到old
        elementData = (E[]) new Object[minCapacity]; // 把elementData擴(kuò)容至minCapacity

        for (int i = 0; i < size; i++) {
            elementData[i] = old[i]; // 還原elementData原來的元素
        }

    }

    // 在List的末尾添加元素赐稽,實(shí)際調(diào)用了add(int index, E element)方法。
    public void add(E element) {
        add(size, element);
    }

    // 在某個(gè)位子添加元素浑侥。
    public void add(int index, E element) {
        CheckRangeForAdd(index); // 數(shù)組越界檢查
        // 如果數(shù)組的容量等于size姊舵,即數(shù)組被填滿,則需要擴(kuò)容
        if (elementData.length == size) {
            ensureCapacity(size + 1); // 擴(kuò)容一個(gè)元素的空間
        }
        for (int i = size; i > index; i--) {
            elementData[i] = elementData[i - 1]; // index及其后面的元素后移
        }
        elementData[index] = element;
        size++;
    }

    // 刪除某位子的元素
    public void remove(int index) {
        CheckRange(index); // 數(shù)組越界檢查
        for (int i = index; i < size - 1; i++) {
            elementData[i] = elementData[i + 1]; // 索引大于index的元素遷移
        }
        size--;
    }

    // 越界檢查寓落,智能訪問0~size-1的范圍
    private void CheckRange(int index) {
        if (index < 0 || index >= size) {
            throw new IndexOutOfBoundsException();
        }
    }

    // 針對add操作的數(shù)組越界檢查括丁,因?yàn)榭梢栽贚ist末尾添加元素,所以其訪問范圍為0~size
    private void CheckRangeForAdd(int index) {
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException();
        }
    }

    // iterator方法伶选,返回一個(gè)MyIterator實(shí)例
    public Iterator<E> iterator() {
        return new MyIterator();
    }

    // MyArrayList的內(nèi)部類史飞,實(shí)現(xiàn)了Iterator接口尖昏,用來實(shí)現(xiàn)遍歷List的操作
    private class MyIterator implements Iterator<E> {
        // 遍歷數(shù)組,默認(rèn)從0開始
        private int currentIndex = 0;

        // 如果沒有超過size祸憋,則說明還有下一元素
        public boolean hasNext() {
            return currentIndex < size;
        }

        // 每一次調(diào)用都返當(dāng)前元素会宪,比如第一調(diào)用next()返回第一元素,第二次調(diào)用則返回第二個(gè)元素
        public E next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            return elementData[currentIndex++];
        }

        // 刪除當(dāng)前元素蚯窥。實(shí)際上是調(diào)用了MyAarrayList的remove方法掸鹅。
        @SuppressWarnings("unused")
        public void remove() {
            MyArrayList.this.remove(--currentIndex);
        }

    }
}

測試代碼####

public static void main(String[] args) {
        MyArrayList<String> myArrayList = new MyArrayList<String>();
        Iterator<String> sIterator = myArrayList.iterator();

        // 在末尾添加元素
        System.out.println("添加元素:b,c,d");
        myArrayList.add("b");
        myArrayList.add("c");
        myArrayList.add("d");

        // 遍歷數(shù)組元素
        System.out.print("iterator遍歷List:");
        while (sIterator.hasNext()) {
            System.out.print(sIterator.next());
        }

        System.out.println();

        // size()
        System.out.println();
        System.out.println("此時(shí)List中元素的個(gè)數(shù)為:" + myArrayList.size());

        System.out.println();

        // 在指定位置添加元素,并輸出結(jié)果
        System.out.println("在List首部添加元素:a");
        myArrayList.add(0, "a");

        System.out.print("iterator遍歷List:");
        sIterator = myArrayList.iterator();
        while (sIterator.hasNext()) {
            System.out.print(sIterator.next());
        }

        System.out.println();

        // 刪除指定位子元素(刪除d)
        System.out.println();
        System.out.println("刪除index為3的元素(d)");
        myArrayList.remove(3);
        System.out.print("iterator遍歷List:");
        sIterator = myArrayList.iterator();
        while (sIterator.hasNext()) {
            System.out.print(sIterator.next());
        }

        System.out.println();
        System.out.println();

        // set()和get()
        System.out.println("修改List的第一個(gè)元素為z.");
        myArrayList.set(0, "z");
        System.out.println("獲取第一個(gè)元素" + myArrayList.get(0));

        System.out.println();

        // iterator.remove()
        System.out.println("測試iterator的remove方法:遍歷List拦赠,每次遍歷刪除當(dāng)前元素");
        Iterator<String> rIterator = myArrayList.iterator();
        while (rIterator.hasNext()) {
            if (rIterator.next() != null) {
                rIterator.remove();
            }
        }
        System.out.println("刪除后的List長度為" + myArrayList.size());
    }

測試結(jié)果####

測試結(jié)果

本篇只給出了ArrayList的一些基本操作巍沙,如果大家想更深入的了解,可以去查看ArrayList的源碼荷鼠,下面是一篇ArrayList源碼分析很好的文章:
http://blog.csdn.net/jzhf2012/article/details/8540410

大家可以思考兩個(gè)問題:

  1. 為什么MyArrayList有了remove方法句携,為什么iterator中還要實(shí)現(xiàn)remove方法。
  2. 為什么MyIterator要作為MyArrayList的內(nèi)部類允乐,還有其他實(shí)現(xiàn)方式嗎矮嫉?
    如果有答案,歡迎大家在評論區(qū)討論牍疏。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蠢笋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鳞陨,更是在濱河造成了極大的恐慌昨寞,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厦滤,死亡現(xiàn)場離奇詭異援岩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)掏导,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門享怀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人趟咆,你說我怎么就攤上這事添瓷。” “怎么了忍啸?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵仰坦,是天一觀的道長履植。 經(jīng)常有香客問我计雌,道長,這世上最難降的妖魔是什么玫霎? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任凿滤,我火速辦了婚禮妈橄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翁脆。我一直安慰自己眷蚓,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布反番。 她就那樣靜靜地躺著沙热,像睡著了一般。 火紅的嫁衣襯著肌膚如雪罢缸。 梳的紋絲不亂的頭發(fā)上篙贸,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機(jī)與錄音枫疆,去河邊找鬼爵川。 笑死,一個(gè)胖子當(dāng)著我的面吹牛息楔,可吹牛的內(nèi)容都是我干的寝贡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼值依,長吁一口氣:“原來是場噩夢啊……” “哼圃泡!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鳞滨,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤洞焙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后拯啦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體澡匪,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年褒链,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了唁情。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡甫匹,死狀恐怖甸鸟,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情兵迅,我是刑警寧澤抢韭,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站恍箭,受9級(jí)特大地震影響刻恭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一鳍贾、第九天 我趴在偏房一處隱蔽的房頂上張望鞍匾。 院中可真熱鬧,春花似錦骑科、人聲如沸橡淑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梁棠。三九已至,卻和暖如春斗埂,著一層夾襖步出監(jiān)牢的瞬間掰茶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工蜜笤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留濒蒋,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓把兔,卻偏偏與公主長得像沪伙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子县好,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容