詳解JAVA高質(zhì)量代碼之?dāng)?shù)組與集合
1.性能考慮,優(yōu)先選擇數(shù)組
數(shù)組在項(xiàng)目開(kāi)發(fā)當(dāng)中使用的頻率是越來(lái)越少,特別是在業(yè)務(wù)為主的開(kāi)發(fā)當(dāng)中,首先數(shù)組沒(méi)有List,Set等集合提供的諸多方法,查找增加算法都要自己編寫,極其繁瑣麻煩,但由于List,Set等集合使用泛型支持后,存放的都為包裝類,而數(shù)組是可以使用基本數(shù)據(jù)類型,而使用基本數(shù)據(jù)類型的執(zhí)行運(yùn)算速度要比包裝類型快得多,而且集合類的底層也是通過(guò)數(shù)組進(jìn)行實(shí)現(xiàn).
2.若有必要,使用變長(zhǎng)數(shù)組
在學(xué)習(xí)集合類當(dāng)中,很多人喜歡將數(shù)組的定長(zhǎng)拿來(lái)和集合類型的自變長(zhǎng)來(lái)做比較,但其實(shí)這種比較并不合適,通過(guò)觀察集合類例如ArrayList的實(shí)現(xiàn)其實(shí)可以看出,所謂的集合變長(zhǎng),其實(shí)只是用婉轉(zhuǎn)的方式對(duì)原數(shù)組進(jìn)行了擴(kuò)容
public static T[] expandCapacity(T[] data, int newLength) {
// 判斷是否為負(fù)值
newLength = newLength < 0 ? 0 : newLength;
// 生成新數(shù)組,拷貝原值并制定長(zhǎng)度
return Arrays.copyOf(data, newLength);
}
當(dāng)性能要求高的時(shí)候,可以考慮使用對(duì)數(shù)組進(jìn)行封裝使用,數(shù)組長(zhǎng)度不變不是我們不使用它們的借口
3.警惕數(shù)組的淺拷貝
數(shù)組的淺拷貝在Java編程中亦是基礎(chǔ)中的基礎(chǔ),淺拷貝是在為數(shù)組拷貝時(shí),基本類型拷貝的是值,而引用類型拷貝的是引用地址,在上面的例子當(dāng)中,拷貝數(shù)組使用的Arrays.copyOf為淺拷貝,在使用時(shí)需要注意
4.在明確的場(chǎng)景下,為集合指定初始容量
在我們平常的使用當(dāng)中,因?yàn)榧项愋褪亲詣?dòng)變長(zhǎng)的,所以基本創(chuàng)建對(duì)象時(shí)不會(huì)為集合類附上初始值,就拿我們最常用的ArrayList來(lái)說(shuō)明,我們首先要知道,當(dāng)集合容量到達(dá)臨界點(diǎn)時(shí),會(huì)將底層的數(shù)組進(jìn)行copyOf的操作,生成新的數(shù)組,而新的數(shù)組容量為舊數(shù)組的1.5倍,而默認(rèn)數(shù)組長(zhǎng)度為10,當(dāng)我們明確知道要放置入容器中的數(shù)據(jù)數(shù)量較多時(shí),應(yīng)該指明初始值,避免多次使用copyOf造成的性能開(kāi)銷
5.選擇合適的最值算法
對(duì)數(shù)據(jù)進(jìn)行最大值或最小值的查找,這是數(shù)據(jù)結(jié)構(gòu)最基本的知識(shí),在Java當(dāng)中我們亦有很多種的方式進(jìn)行實(shí)現(xiàn),以下列舉2種算法
public static int getMaxByArray(int[] data) {
// 最簡(jiǎn)單自行實(shí)現(xiàn)的查找方式
int max = data[0];
for (int i = 1, size = data.length; i < size; i++) {
max = max < i ? i : max;
}
return max;
}
public static int getMaxByArray(int[] data) {
// 先排序后獲取最后位
Arrays.sort(data);
return data[data.length - 1];
}
6.基本類型數(shù)組轉(zhuǎn)換陷阱!
請(qǐng)觀察以下代碼
public static void main(String[] args) {
int[] nums = new int[] { 1, 2, 3, 4, 5 };
List list = Arrays.asList(nums);
System.out.println(list.size());
// 此時(shí)輸出的size為1
}
我們期望的結(jié)果是將數(shù)組中的元素通過(guò)Arrays.asList轉(zhuǎn)換到集合類當(dāng)中,但事與愿違,我們只將數(shù)組本身增加了進(jìn)入,并沒(méi)有將數(shù)組內(nèi)的值分拆分開(kāi)來(lái),此時(shí)若然對(duì)集合List增加了泛型就會(huì)在編譯期間給出錯(cuò)誤的提示,或?qū)?shù)組本身改變成Integer就可以解決問(wèn)題
7.asList方法產(chǎn)生的List對(duì)象不可更改
通過(guò)上面的例子,我們可以看到使用Arrays.asList方法可以將一個(gè)數(shù)組轉(zhuǎn)換成一個(gè)List,那通過(guò)asList方法返回的List有什么特別呢?注意,這個(gè)返回的List是不支持更改的,原因是因?yàn)閍sList方法返回的,并不是java.util.ArrayList,而是Arrays工具類中的一個(gè)靜態(tài)私有內(nèi)部類,雖然都有實(shí)現(xiàn)和ArrayList一樣的父類AbstractList,但在復(fù)寫add等方法時(shí),卻是拋出了UnsupportedOperationException,這個(gè)靜態(tài)私有內(nèi)部類只實(shí)現(xiàn)了size,toArray,get,contains這幾個(gè)方法
8.對(duì)不同的數(shù)據(jù)結(jié)構(gòu)使用不同的遍歷方式
請(qǐng)觀看以下代碼
public static void main(String[] args) {
// 以下為ArrayList集合的遍歷方式
int num = 80 * 10000;
List arrayList = new ArrayList(num);
for (int i = 0, size = arrayList.size(); i < size; i++) {
arrayList.get(i);
}
// 以下為L(zhǎng)inkedList集合的遍歷方式
List linkedList = new LinkedList();
for (Integer integer : linkedList) {
}
}
為什么對(duì)LinkedList和ArrayList要選擇不同的遍歷方式?
1.因?yàn)锳rrayList實(shí)現(xiàn)了RamdomAccess接口(隨機(jī)存取接口),RamdomAccess接口和Serializable,Cloneable接口一樣是Java中的標(biāo)示接口,代表這個(gè)這個(gè)類可以隨機(jī)存取,對(duì)ArrayList來(lái)說(shuō)就標(biāo)志著,數(shù)據(jù)之間沒(méi)有關(guān)聯(lián),即相鄰的兩個(gè)位置沒(méi)有互相依賴的關(guān)系,可以隨機(jī)訪問(wèn),
2.Java中的foreach語(yǔ)法是iterator(迭代器)的變形用法,我們知道迭代器是23種設(shè)計(jì)模式的一種,但迭代器是需要知道兩個(gè)元素時(shí)間的關(guān)系的,不然怎么提供hasNext的支持呢?就是因?yàn)樯弦粋€(gè)元素要判斷下一個(gè)元素是否存在,強(qiáng)行建立了這種關(guān)系,違背了ArrayList隨機(jī)存取的特別
3.在LinkedList中,因?yàn)槭峭ㄟ^(guò)雙向鏈表的形式來(lái)存儲(chǔ),所以對(duì)迭代器的支持非常好,因?yàn)長(zhǎng)inkedList相鄰的兩個(gè)元素本來(lái)就存在關(guān)系所以在對(duì)LinkedList和ArrayList要采取不同的遍歷方式,讀者若然有興趣可以嘗試一下對(duì)LinkedList采用下標(biāo)的形式訪問(wèn),會(huì)發(fā)現(xiàn)兩者的效率有較大的差距
8.適時(shí)選擇ArrayList或LinkedList
ArrayList和LinkedList的主要區(qū)別:
1.ArrayList底層的數(shù)據(jù)結(jié)構(gòu)為數(shù)組,而LinkedList底層結(jié)構(gòu)為雙向鏈表
2.在插入數(shù)據(jù)時(shí),由于ArrayList每次插入后都需要將數(shù)組元素向后順延位置,而LinkedList只需要更改頭節(jié)點(diǎn)和尾節(jié)點(diǎn)即可完成插入操作,所以在插入操作較為頻繁時(shí),優(yōu)先使用LinkedList
3.在刪除數(shù)據(jù)時(shí),由于ArrayList要保持?jǐn)?shù)組的有序性,當(dāng)刪除后元素要亦需要向后或向前移位,而LinkedList照舊還是更改頭尾節(jié)點(diǎn).
4.在更新時(shí),由于LinkedList會(huì)使用折半遍歷的方式進(jìn)行查找定位元素再進(jìn)行更新,對(duì)比起ArrayList的直接定位下標(biāo)元素替換,ArrayList對(duì)更新的效率更佳
5.LinkedList可以模擬隊(duì)列,通過(guò)LinkedList的addFirst,addLast等操作
9.列表相等只需關(guān)心元素?cái)?shù)據(jù)
Java為了我們可以安心的面向List,Set,Map等接口進(jìn)行編程,因此對(duì)集合類中的equlas進(jìn)行了復(fù)寫,讓我們?cè)诒容^兩個(gè)集合是否相等時(shí),只需要比較元素?cái)?shù)據(jù)是否相等即可,避免了因?yàn)樘鎿Q集合實(shí)現(xiàn)類造成的錯(cuò)誤Java代碼
public static void main(String[] args) {
List arrayList = new ArrayList();
arrayList.add(1);
arrayList.add(2);
List linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
System.out.println(arrayList.equals(linkedList));
// 不用關(guān)心具體實(shí)現(xiàn),輸出為true
}
相關(guān)文章
Springboot 2.x集成kafka 2.2.0的示例代碼
kafka近幾年更新非常快,也可以看出kafka在企業(yè)中是用的頻率越來(lái)越高。本文主要為大家介紹了Springboot 2.x集成kafka 2.2.0的示例代碼,需要的可以參考一下2022-04-04
Java實(shí)現(xiàn)儲(chǔ)存對(duì)象并按對(duì)象某屬性排序的幾種方法示例
這篇文章主要介紹了Java實(shí)現(xiàn)儲(chǔ)存對(duì)象并按對(duì)象某屬性排序的幾種方法,結(jié)合實(shí)例形式詳細(xì)分析了Java儲(chǔ)存對(duì)象并按對(duì)象某屬性排序的具體實(shí)現(xiàn)方法與操作注意事項(xiàng),需要的朋友可以參考下2020-05-05
教你如何把Eclipse創(chuàng)建的Web項(xiàng)目(非Maven)導(dǎo)入Idea
這篇文章主要介紹了教你如何把Eclipse創(chuàng)建的Web項(xiàng)目(非Maven)導(dǎo)入Idea,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04
Java中策略設(shè)計(jì)模式的實(shí)現(xiàn)及應(yīng)用場(chǎng)景
策略設(shè)計(jì)模式是Java中一種常用的設(shè)計(jì)模式,它通過(guò)定義一系列算法并將其封裝成獨(dú)立的策略類,從而使得算法可以在不影響客戶端的情況下隨時(shí)切換。策略設(shè)計(jì)模式主要應(yīng)用于系統(tǒng)中存在多種相似的算法、需要靈活調(diào)整算法邏輯或者需要擴(kuò)展新的算法等場(chǎng)景2023-04-04

