對Java程序開發(fā)而言脯爪,ArrayList 的使用頻率是非常高的癣朗,尤其在進行 JavaWeb 開發(fā)的時候,ArrayList 和 HashMap 這兩個類浇辜,相信你一定不會陌生朋魔,因為天天都在用嘛岖研。
本系列對 ArrayList 做一個解析,同時把Java基礎知識個串連進去警检。一開始我會對如何使用 ArrayList 做一個簡要的說明孙援,然后,我們來仿照 ArrayList 封裝一個自己的集合框架MyList扇雕,通過練習拓售,來一步一步猜想ArrayList 可能的實現方式。
最后镶奉,深入到ArrayList 的源碼進行解讀础淤。
為什么要學習源碼?
很簡單哨苛,一個知道源碼的人和一個不知道源碼的人鸽凶,雖然都能使用 ArrayList ,但是建峭,他們在使用的時候玻侥,心態(tài)是完全不一樣的。
只有當你深入了源碼亿蒸,然后你才會對它的一些細節(jié)有更充分的認識凑兰。這是一本萬利的事情。
當然边锁,對于初學者姑食,還是盡量以使用為主,因為源碼的話砚蓬,畢竟有一定的難度矢门。如果一味地追求這些東西,可能會大大降低自己的學習興趣和熱情灰蛙。
1祟剔、 ArrayList 概述
副本難度:一顆星
經驗值:500
首先來看一下文檔,
<h5>
All Implemented Interfaces:
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess
</h5>
<p>
從圖中可以看到摩梧,ArrayList 實現了Iterable接口物延,這個接口表示一種迭代的能力。
</p>
既然是ArrayList仅父,那么肯定和 List 有關叛薯,所以它果然繼承了List接口。
接口的知識點在這里就用上了笙纤。
其他接口我們暫且不談耗溜,先繼續(xù)。
然后省容,我們看一下官方對ArrayList 做出的說明抖拴。
文檔中有這么一句:
Resizable-array implementation of the List interface.
這句話是說,ArrayList 是對List接口的一個實現腥椒,實現方式是利用一個可改變尺寸的數組阿宅,也就是說,它的底層就是一個數組笼蛛。而且是可改變尺寸的數組洒放,說明這個數組是動態(tài)的。
哦滨砍,難怪它叫ArrayList 往湿,Array就是數組的意思,那么它肯定和數組有關惋戏。
**Implements all optional list operations, and permits all elements, including null. **
ArrayList 實現了list接口的所有方法领追,并且允許空元素。
前半句是肯定的日川,因為在Java中蔓腐,如果一個類實現了一個接口,那么就必須要重寫該接口里所有的抽象方法龄句。
我們知道回论,接口里只有方法的聲明,沒有方法的實現分歇。
我對接口的理解傀蓉,總結以后就只有一句話:
Java類實現接口,就是給這個類本身添加了一個新的身份职抡。
ArrayList是一個類葬燎,那么它的身份就是ArrayList,你問他“你叫什么名字呀?”谱净,他肯定會毫不猶豫地告訴你窑邦,“我叫ArrayList!”壕探。
白天冈钦,我們大家都以為它是ArrayList,結果到了夜晚李请。
ArrayList竟然搖身一變瞧筛,成為了Iterable!
原來导盅,ArrayList實現了Iterable较幌,所以它也就擁有了Iterable的身份。
接口其實就是這個意思白翻。網上對接口的解釋眾說紛紜乍炉,反正我總結下來就是這么一句話,也不想搞那么復雜了嘁字。
ArrayList不僅有Iterable的身份恩急,還擁有其他好幾種身份,比如List纪蜒,Collection等衷恭。
這個情況也叫作多態(tài)。
新建一個測試類纯续。
public class TestArrayList1 {
public static void main(String[] args) {
}
}
現在我新建一個ArrayList類的實例
ArrayList list = new ArrayList();
這肯定是沒問題的随珠。
文檔上說,ArrayList 實現了Iterable接口猬错,那么也就是說窗看,它的另一個身份是Iterable。
所以倦炒,我這樣寫是不是也沒有問題啊显沈。
Iterable iterable = new ArrayList();
就好像白天是普通的上班族,一旦到了夜晚逢唤,就拉讯。。鳖藕。
總之魔慷,
Iterable iterable = new ArrayList();
這句話的含義就是說,它原來是普通的ArrayList著恩,一旦情況需要院尔,就變身成為Iterable蜻展。
好了,既然變成了 Iterable 邀摆,那么它是不是也就擁有了 Iterable 的能力纵顾?
我們來看一下 Iterable 有哪些方法?
去除繼承自Object類的方法不談隧熙,Iterable 只有一個方法片挂,就是 iterator()
它返回的是一個 Iterator 幻林,這是一個迭代器贞盯。
通過這個迭代器,我們是不是就可以遍歷 ArrayList 中所有的數據了呀沪饺?
首先躏敢,我們需要一個迭代對象:
Iterable iterable = new ArrayList();
Iterator it = iterable.iterator();
當然,這個例子中整葡,ArrayList里面沒有數據件余。
現在思考一個問題,是不是我非得把 ArrayList 對象改變成 Iterable 身份遭居,才可以調用 iterator() 方法呢啼器?
當然不是了,ArrayList 有一個身份是 Iterable 俱萍,所以它具有 Iterable 的所有能力(方法)端壳,這沒問題。那么難道 ArrayList 不變身枪蘑,就沒有 Iterable 的能力了嗎损谦?
答案自然是否定的。
一個小說家如果有一天轉行去寫代碼了岳颇,那么你覺得他還會不會寫小說呢照捡?
肯定會嘛,這沒有什么好懷疑的话侧。
所以栗精,代碼這么寫是不是也沒關系?
ArrayList list = new ArrayList();
for (int i = 0; i < 100; i++) {
list.add(i);
}
Iterator it = list.iterator();
循環(huán)輸出
while(it.hasNext()){
System.out.println(it.next());
}
總結一下瞻鹏,ArrayList 它只要實現 Iterable 接口悲立,那么它就必須要擁有 Iterable 規(guī)定的所有能力,也就是方法乙漓。而接口中级历,我們知道它里面只有方法的聲明,沒有方法的實現叭披,所以 ArrayList 需要實現這些方法寥殖,實現接口就是這么個意思玩讳。
2、 ArrayList常用方法
副本難度:三顆星
經驗值:800
2.1嚼贡、 屬性和方法的調用問題
在調用ArrayList的方法之前熏纯,我們需要先獲得一下ArrayList的實例對象,除了靜態(tài)方法粤策,其他所有的方法武福,都只能由對象來調用搜锰。
ArrayList是一個類,我更愿意把它稱為一個** 數據模板**。它只是一個模板而已赔蒲,不是一個實實在在的對象脖阵,這一點首先要確定烫葬。
就好像工廠生產一個產品牺陶,首先是不是要有一個模板和設計圖紙,這個模板決定了你這個產品是一種怎樣的形狀愈魏,以及可能會具備哪些功能觅玻?圖紙則決定了功能的具體實現。
比如生成一部手機培漏,模板開出來就是一個扁平的長方體的樣子溪厘,可是光有模板還不行,你還得規(guī)定它的一些具體細節(jié)牌柄。
這些細節(jié)就好比是Java類的構造方法畸悬,以及其他的一些方法實現。
但是友鼻,你光給客戶模板和圖紙行嗎傻昙?
一般來說是不行的。
至于靜態(tài)方法彩扔,我們知道妆档,我們調用靜態(tài)方法的時候,不需要先生成一個實例虫碉,可以通過類名直接調用贾惦。
這就相當于,在弄模版的時候敦捧,這些功能就已經定制在里面了须板。
你買手機的時候,里面不是經常有一些內置的應用嗎兢卵?有些刪都刪不掉习瑰,這不就相當于靜態(tài)方法嗎?
(我只是舉一個例子啊秽荤,你不要非得較真說我可以ROOT一下疤鹧佟)
如果模板里面已經有了一些做好的功能柠横,今后任何根據這個模具生成出來的產品也自帶了這些功能。
如果模板里面已經做好了一些功能课兄,那么我的確可以使用這個模板牍氛,而不需要真正拿到一個產品。
比如生產一部手機烟阐,它的模板里面已經做好了一個手電筒的功能搬俊,那么,你即便不給我一個真正的產品蜒茄,僅僅給我一個模板唉擂,我是不是也可以用它的手電筒功能呢?
這就是靜態(tài)方法扩淀。
所以我們常說楔敌,靜態(tài)方法和靜態(tài)屬性為所有實例共用,不就是這個道理嗎驻谆?
所以,正常情況下庆聘,我們調用一個類的非靜態(tài)方法胜臊,是不是必須要先new一個對象?
好的伙判,我們現在來 根據 ArrayList 模板生產一個 ArrayList 產品象对。
這樣,我們才能調用它里面所有的非私有方法宴抚。
怎么生產呢勒魔,是不是new一下就可以啦?
ArrayList arrayList = new ArrayList();
2.2菇曲、 add方法
ArrayList是一個集合冠绢,既然是一個集合,那么它肯定是可以往里頭添東西的常潮。
怎么往里面添弟胀,用add,用add方法往里面加喊式。
arrayList.add("HelloWorld"); //添加一個字符串
arrayList.add(new Integer(100));//添加一個Integer類型的數字
add的參數就是一個object孵户,這就是多態(tài),多態(tài)就是多種形態(tài)岔留,多種身份的意思夏哭。object可以有多種形態(tài)。它可以是String献联,也可以是Integer竖配,還可以是用戶自定義的類型厕吉。
這里是不是又多態(tài)了。械念。头朱。
這就是多態(tài)的一個用法。
2.3龄减、 get 方法
既然能夠往里面添加東西项钮,是不是肯定還要拿出來啊。
怎么拿出來希停,用get方法拿出來烁巫,而且一次只能拿一個。不要多拿哦宠能。
get 方法需要傳入一個 int 類型的數字亚隙,這個數字就是元素對應的下標。
我們剛才第一個放進去的是 "HelloWorld" 违崇,一個字符串阿弃。那么對應的下標就是0 。
第二個放進去的是new Integer(100)羞延,這是一個Integer對象渣淳,是一個實實在在的東西了。那么對應的下標就是1 伴箩。
現在入愧,我取出第1個元素,應該是100嗤谚。
試試看
Integer i = arrayList.get(1);
報錯了棺蛛,因為 get 方法返回的是一個Object對象,而我們拿Integer 去接巩步,就出問題了旁赊。
這是咋回事呢?
很簡單渗钉,比如張三是一個醫(yī)生彤恶,同時他還擁有一個人類的身份,可并不是所有的人類都是醫(yī)生蚌佟声离?
注意我下面分析的用詞,能幫助你理解瘫怜。
同理术徊,Integer 是一個整數類型,同時它還擁有一個Object的身份鲸湃,可并不是所有的 Object 都是Integer 霸獭子寓?
同接口一樣,A繼承B笋除,可以看成A同時擁有了B的身份斜友。
如果A繼續(xù)保持A的身份,那么它不僅擁有自己本身的能力垃它,也擁有B的能力鲜屏。只要A愿意,他完全可以展現B的能力国拇。
如果A變身成為了B洛史,那么它肯定不希望別人知道他是A。
就好比雖然他知道他可以變身成為奧特曼酱吝,但是一般情況下也殖,他都不愿意曝露自己的身份。
所以如果A變身成為了B务热,那么A就只會使用B的能力忆嗜,同時隱藏他本身的能力。
如果你是初學者陕习,請仔細體會一下這其中的韻味霎褐。慢慢地,你就會對多態(tài)有一個更深入的理解该镣。一段時間后,你再重新去看以前寫的代碼响谓,會有不一樣的感覺损合。
再舉一個例子,幫助你理解娘纷。
我 new 一個 ArrayList :(在eclipse中)
ArrayList arrayList = new ArrayList();
換行嫁审,寫上arrayList 。
我直接在arrayList 右邊加一個點赖晶,然后會有提示:
這些都是它可以調用的方法和屬性律适,哇,這么多遏插。
如果我這樣寫呢捂贿?
Object arrayList = new ArrayList();
我也在arrayList 右邊加一個點
不好意思,你現在只能調用 Object 類的屬性和方法了胳嘲。
嗯厂僧,再體會一下。
初學者在面向對象方面的理解總是會走彎路了牛,如果你能把這些東西理清颜屠,對今后的學習會有巨大的好處辰妙。
繼續(xù)。
我們這里就強轉一下吧甫窟,因為我們知道 index 為1的元素是一個Integer類型的密浑。
Integer i = (Integer) arrayList.get(1);
System.out.println(i);
結果
好的,是正確的粗井。
2.4尔破、 remove 方法
remove 方法可以刪除集合中的元素,ArrayList給我們提供了很多刪除元素的方法背传。
我們這里先看一下第一個方法呆瞻。
這是通過數組下標來刪除某一個特定的元素,我們剛才給ArrayList添加了兩個元素径玖,下標分別為 0,1 痴脾,那么,如果我刪除第0個元素梳星,會怎么樣呢赞赖?
首先,ArrayList的列表長度會不會改變冤灾?
我們可以通過size()方法來獲取ArrayList當前的列表長度前域。
測試:
arrayList.remove(0);
System.out.println(arrayList.size());
打印出來是1,看來的確是影響長度了韵吨。
2.5匿垄、 toArray 方法
這個方法可以將ArrayList轉換成一個Object數組。
例:
Object[] objs = arrayList.toArray();
for (int i = 0; i < objs.length; i++) {
System.out.println(objs[i].toString());
}
需要注意的是归粉,哪怕你給ArrayList全部添加Integer類型的元素椿疗,也不能采用這樣的代碼:
Integer[] objs = (Integer[]) arrayList.toArray();
這是錯誤的,雖然編譯的時候不會報錯糠悼,但是運行無法通過届榄。
因為ArrayList的add方法可以添加任意類型的參數,Java運行機制無法獲知ArrayList中的元素是否可以都強制轉換為你指定的類型倔喂。所以這種寫法是不被允許的铝条。