Java 泛型數(shù)組列表 ArrayList(學(xué)習(xí) Java 編程語言 051)

雖然 Java 中允許在運(yùn)行時確定數(shù)組的大小。
?int size = ...;
?String[] staff = new String[size];
但是并沒有完全解決運(yùn)行時動態(tài)更改數(shù)組的問題嗤练。

一旦確定了數(shù)組的大小,就不能很容易地改變它立砸。在 Java 中番甩,解決這個問題最簡單的方法是使用 Java 中另外一個類咆蒿,名為 ArrayList堪遂。ArrayList 類類似于數(shù)組尉尾,但在添加或刪除元素時爆阶,它能夠自動地調(diào)整數(shù)組容量,而不要為此編寫任何代碼沙咏。

ArrayList 是一個有類型參數(shù)(type parameter) 的泛型類(generic class)辨图。為了指定數(shù)組列表保存的元素對象的類型,需要用一對尖括號將類名括起來追加到 ArrayList 后面肢藐,例如 ArrayList<String>故河。

1. 聲明數(shù)組列表

聲明和構(gòu)造一個保存 String 對象的數(shù)組列表:
?ArrayList<String> list = new ArrayList<String>();
在 Java 10 中,最好使用 var 關(guān)鍵字以避免重復(fù)寫類名:
?var list = new ArrayList<String>();
如果沒有使用 var 關(guān)鍵字吆豹,可以省略右邊的類型參數(shù):
?ArrayList<String> list = new ArrayList<>();
這稱為 “菱形” 語法鱼的,因為空尖括號 <> 就像是一個菱形理盆。可以結(jié)合 new 操作符使用菱形語法凑阶。編譯器會檢查新值要什么猿规。如果賦值給一個變量,或傳遞到某個方法宙橱,或者從某個方法返回姨俩,編譯器會檢査這個變量、參數(shù)或方法的泛型類型师郑,然后將這個類型放在 <> 中环葵。在這個例子中,new ArrayList<>() 將賦至一個類型為 ArrayList<String> 的變量宝冕,所以泛型類型為 String

警告: 如果使用 var 聲明 ArrayList张遭,就不能使用菱形語法。以下聲明:
?var list = new ArrayList<>();
會生成一個 ArrayList<Object>猬仁。

注釋: Java 5 以前的版本沒有提供泛型類帝璧,而是有一個保存 Object 類型元素的 ArrayList 類,它是一個“自適應(yīng)大小”(one-size-fits-all)的集合湿刽。仍然可以使用沒有后綴 <...> 的 ArrayList,這將被認(rèn)為是刪去了類型參數(shù)的一個“原始”類型褐耳。

注釋: 在 Java 的老版本中诈闺,程序員使用 Vector 類實現(xiàn)動態(tài)數(shù)組。不過铃芦,ArrayList 類更加高效雅镊,沒有任何理由再使用 Vector 類。

使用 add 方法可以將元素添加到數(shù)組列表中刃滓。例如仁烹,下面展示了如何將 String 對象添加到一個數(shù)組列表中:

ArrayList<String> staff = new ArrayList<String>();
staff.add(new String("Harry Hacker"));
staff.add(new String("Tommy Tester"));

數(shù)組列表管理著一個內(nèi)部的對象引用數(shù)組。最終咧虎,數(shù)組的全部空間有可能被用盡卓缰。這時就顯現(xiàn)出數(shù)組列表的魅力了:如果調(diào)用 add 且內(nèi)部數(shù)組已經(jīng)滿了,數(shù)組列表就會自動地創(chuàng)建一個更大的數(shù)組砰诵,并將所有的對象從較小的數(shù)組中拷貝到較大的數(shù)組中征唬。

如果已經(jīng)清楚或能夠估計出數(shù)組可能存儲的元素數(shù)量,就可以在填充數(shù)組之前調(diào)用 ensureCapacity 方法:
?list.ensuteCapacity(100);
這個方法調(diào)用將分配一個包含 100 個對象的內(nèi)部數(shù)組茁彭。這樣一來总寒,前 100 次 add 調(diào)用不會帶來開銷很大的重新分配空間。

還可以把初始容量傳遞給 ArrayList 構(gòu)造器:
?ArrayList<String> list = new ArrayList<>(100);

警告: 如下分配數(shù)組列表:
?new ArrayList<>(100) // capacity is 100
這與分配一個新數(shù)字有所不同:
?new String[100] // size is 100
數(shù)組列表的容量與數(shù)組的大小有一個非常重要的區(qū)別理肺。如果分配一個有 100 個元素的數(shù)組摄闸。數(shù)組就有 100 個空位置(槽)可以使用善镰。而容量為 100 個元素的數(shù)組列表只是可能保存 100 個元素(實際上也可以超過 100,不過要以重新分配空間為代價)年枕,但是在最初媳禁,甚至完成初始化構(gòu)造之后,數(shù)組列表不包含任何元素画切。

size 方法將返回數(shù)組列表中包含的實際元素個數(shù)竣稽。
?list.size()
將返回 staff 數(shù)組列表的當(dāng)前元素個數(shù),它等價于數(shù)組 a 的 a.length霍弹。

一旦能夠確認(rèn)數(shù)組列表的大小將保持恒定毫别,不再發(fā)生變化,就可以調(diào)用 trimToSize 方法典格。這個方法將存儲塊的大小調(diào)整為保存當(dāng)前元素數(shù)量所需要的存儲空間岛宦。垃圾回收器將回收多余的存儲空間。

一旦消減了數(shù)組列表的大小耍缴,添加新元素就需要花時間再次移動存儲塊砾肺,所以應(yīng)該在確認(rèn)不會再向數(shù)組列表添加任何元素時再調(diào)用 trimToSize。

java.util.ArrayList<E> 1.2

  • ArrayList<E>

    構(gòu)造一個空數(shù)組列表防嗡。

  • boolean add(E obj)

    在數(shù)組列表的末尾追加一個元素变汪。永遠(yuǎn)返回 true。

  • int size()

    返回當(dāng)前存儲在數(shù)組列表中元素個數(shù)蚁趁。(當(dāng)然裙盾,這個值永遠(yuǎn)不會大于數(shù)組列表的容量)

  • void ensureCapacity()

    確保數(shù)組列表不重新分配內(nèi)部存儲數(shù)組的情況下有足夠的容量存儲給定數(shù)量的元素。

  • void trimToSize()

    將數(shù)組列表的存儲容量削減到當(dāng)前大小他嫡。

2. 訪問數(shù)組列表元素

數(shù)組列表自動擴(kuò)展容量的便利增加了訪問元素語法的復(fù)雜程度番官。其原因是 ArrayList 類并不是 Java 程序設(shè)計語言的一部分;它只是由某個人編寫并在標(biāo)準(zhǔn)庫中提供的一個實用工具類钢属。

數(shù)組列表使用 get 和 set 方法訪問或改變數(shù)組列表的元素徘熔。

警告: 只有當(dāng)前數(shù)組列表的大小大于 i 時,才能夠調(diào)用 list.set(i, x)淆党。例如這段代碼時錯誤的:
?var list = new ArrayList<String>(100); // capacity 100, size 0
?list.set(0, x);

要使用 add 方法為數(shù)組添加新元素酷师,而不是 set 方法,set 方法只是用來替換數(shù)組列表中已經(jīng)加入的元素宁否。

要得到一個數(shù)組列表的元素窒升,使用 get 方法:
?String srt = list.get(i);

注釋: 沒有泛型時,原始的 ArrayList 類提供的 get 方法別無選擇慕匠,只能返回 Object饱须,因此,get 方法的調(diào)用者必須對返回值進(jìn)行強(qiáng)制類型轉(zhuǎn)換:
?String str = (String) list.get(i);
原始的 ArrayList 還存在一定的危險性台谊。它的 add 和 set 方法接受任意類型的對象蓉媳。對于下面這個調(diào)用:
?list.set(i, new Date());
它能正常編譯而不會給出任何警告譬挚,只有在檢索對象并試圖對它進(jìn)行強(qiáng)制類型轉(zhuǎn)換時,才會發(fā)現(xiàn)有問題酪呻。如果使用 ArrayList<String>减宣,編譯器就會檢測到這個錯誤。

這個技巧可以一舉兩得玩荠,即可以靈活地擴(kuò)展數(shù)組漆腌,又可以方便地訪問數(shù)組列表。首先阶冈,創(chuàng)建一個數(shù)組列表闷尿,并添加所有的元素。

var list = new ArrayList<X>();
while (...) {
    x = ...;
    list.add(x);
}  

執(zhí)行完上述操作后女坑,使用 toArray 方法將數(shù)組元素拷貝到一個數(shù)組中填具。
?var a = new X[list.size()];
?list.toArray(a);

有時需要在數(shù)組列表的中間插入元素,為此可以使用 add 方法并提供一個索引參數(shù)匆骗。
?var str = "";
?int n = staff.size() / 2;
?list.add(n, str);
位置 n 及以后的元素都要向后移動一個位置劳景,為新元素留出空間。插入新元素后碉就,如果數(shù)組列表新的大小超過了容量盟广,數(shù)組列表就會重新分配它的存儲數(shù)組。

可以從數(shù)組列表中間刪除一個元素:
?String srt = list.remove(n);
位于這個位置之后的所有元素都向前移動一位铝噩,并且數(shù)組的大小減 1衡蚂。

插入和刪除元素的操作效率很低。對于較小的數(shù)組列表來說骏庸,不必?fù)?dān)心這個問題。但如果存儲的元素比較多年叮,又經(jīng)常需要在中間插入具被、刪除元素,就應(yīng)該考慮使用鏈表了只损。

可以使用 “for each” 循環(huán)遍歷數(shù)組列表的內(nèi)容:

for (String str : list) {
    do something with str
}

這個循環(huán)和下列代碼具有相同的效果:

for (int i = 0; i < list.size(); i++) {
    String str = list.get(i);
    do something with str
}

使用數(shù)組列表情注意下面的變化:

  • 不必指定數(shù)組列表的大小一姿。
  • 使用 add 將任意多的元素添加到數(shù)組列表中。
  • 使用 size() 而不是 length 統(tǒng)計元素個數(shù)跃惫。
  • 使用 list.get(i) 而不是 list[i] 來訪問元素叮叹。

java.util.ArrayList<E> 1.2

  • E set(int index, E obj)

    將值 obj 放置在數(shù)組列表的指定索引位置,返回之前的內(nèi)容爆存。

  • E get(i)

    得到指定索引位置存儲的值蛉顽。

  • void add(int index, E obj)

    后移元素從而將 obj 插入到指定索引位置。

  • E remove(int index)

    刪除指定索引位置的元素先较,并將后面的所有元素前移携冤。返回所刪除的元素悼粮。

3. 類型化與原始數(shù)組列表的兼容性

假設(shè)有下面這個遺留下來的類:

public class EmployeeDB
{
    public void update(ArrayList list) { . . . }
    public ArrayList find(String query) { . . . }
}

public class Employee {
    private String name;
    private double salary;
    private LocalDate hireDay;

    public Employee(String name, double salary, int year, int month, int day) {
        this.name = name;
        this.salary = salary;
        hireDay = LocalDate.of(year, month, day);
    }
}

可以將一個類型化的數(shù)組列表傳遞給 update 方法,而并不需要進(jìn)行任何類型轉(zhuǎn)換曾棕。

EmployeeDB employeeDB = new employeeDB();

ArrayList<Employee> list = . . .;
employeeDB.update(list);

可以將 list 對象傳遞給 update方法扣猫。

相反地,將一個原始 ArrayList 賦給一個類型化 ArrayList 會得到一個警告翘地。
?ArrayList<String> result = employeeDB.find(query); // yields warning
注釋: 為了能夠看到警告的文字信息申尤,要將編譯選項置為 -Xlint:unchecked

使用強(qiáng)制類型轉(zhuǎn)換并不能避免出現(xiàn)警告衙耕。
?ArrayList<String> result = (ArrayList<Employee>) employeeDB.find(query);
??// yields another warning
這樣將會得到另外一個警告信息昧穿,指出類型轉(zhuǎn)換有誤。

警告: 盡管編譯器沒有給出任何錯誤信息或警告臭杰,但是這樣調(diào)用并不太安全粤咪。在 update 方法中,添加到數(shù)組列表中的元素可能不是 Employee 類型渴杆。訪問這些元素時就會出現(xiàn)異常寥枝。 聽起來似乎很嚇人,但思考一下就會發(fā)現(xiàn)磁奖,這種行為與 Java 中引入泛型之前是一樣的囊拜,虛擬機(jī)的完整性并沒有受到威脅。在這種情形下比搭,既沒有降低安全性冠跷,也沒有受益于編譯時的檢查。

這就是 Java 中不盡如人意的泛型類型限制所帶來的結(jié)果身诺。出于兼容性的考慮蜜托,編譯器檢查到?jīng)]有發(fā)現(xiàn)違反規(guī)則的現(xiàn)象之后,就將所有的類型化參數(shù)列表轉(zhuǎn)換成原始 ArrayList 對象霉赡。在程序運(yùn)行時橄务,所有的數(shù)組列表都是一樣的,即虛擬機(jī)中沒有類型參數(shù)穴亏。因此蜂挪,強(qiáng)制類型轉(zhuǎn)換(ArrayList)和(ArrayList<Employee>)將執(zhí)行相同的運(yùn)行時檢查。

在這種情形下嗓化,你并不能做什么棠涮。在與遺留的代碼交互時,要研究編譯器的警告刺覆,確保這些警告不太嚴(yán)重就行了严肪。

一旦確保問題不太嚴(yán)重,可以用 @SuppressWamings("unchecked") 注解來標(biāo)記接受強(qiáng)制類型轉(zhuǎn)換的變量,如下所示:

@SuppressWarnings("unchecked") ArrayList<String> result = 
        (ArrayList<String>) employeeDB.find(query); // yields another warning
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诬垂,一起剝皮案震驚了整個濱河市劲室,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌结窘,老刑警劉巖很洋,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異隧枫,居然都是意外死亡喉磁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來品抽,“玉大人,你說我怎么就攤上這事孕暇〕嘈耍” “怎么了桶良?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵陨帆,是天一觀的道長疲牵。 經(jīng)常有香客問我,道長矢洲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任袁滥,我火速辦了婚禮题翻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘塑荒。我一直安慰自己姜挺,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布凌箕。 她就那樣靜靜地躺著词渤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪芜壁。 梳的紋絲不亂的頭發(fā)上高氮,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天腰涧,我揣著相機(jī)與錄音窖铡,去河邊找鬼费彼。 笑死口芍,一個胖子當(dāng)著我的面吹牛鬓椭,可吹牛的內(nèi)容都是我干的小染。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼资盅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起呵扛,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎缤灵,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荣赶,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年拔创,在試婚紗的時候發(fā)現(xiàn)自己被綠了利诺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡慢逾,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情变擒,我是刑警寧澤策添,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布毫缆,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏旺拉。R本人自食惡果不足惜产上,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蛾狗。 院中可真熱鬧蒂秘,春花似錦、人聲如沸淘太。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒲牧。三九已至撇贺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間冰抢,已是汗流浹背松嘶。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挎扰,地道東北人翠订。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像遵倦,于是被迫代替她去往敵國和親尽超。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

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