組插入操作詳解:從手動(dòng)位移到標(biāo)準(zhǔn)庫(kù)函數(shù)的高效實(shí)現(xiàn))
1. 數(shù)組插入操作一個(gè)看似簡(jiǎn)單卻暗藏玄機(jī)的基礎(chǔ)功在編程世界里數(shù)組大概是每個(gè)開(kāi)發(fā)者最早接觸到的數(shù)據(jù)結(jié)構(gòu)之一。它簡(jiǎn)單、直觀就像一排整齊的儲(chǔ)物柜每個(gè)格子元素都有一個(gè)固定的編號(hào)索引。但當(dāng)我們想在這排柜子中間塞進(jìn)一個(gè)新東西時(shí)問(wèn)題就來(lái)了——這排柜子是固定死的沒(méi)法憑空變出一個(gè)新位置。這就是“在數(shù)組中插入一個(gè)元素”這個(gè)操作之所以成為一個(gè)經(jīng)典面試題和日常高頻操作的根本原因。它考察的不僅僅是你對(duì)語(yǔ)法是否熟悉更考驗(yàn)?zāi)銓?duì)計(jì)算機(jī)內(nèi)存模型、數(shù)據(jù)操作成本以及不同場(chǎng)景下方案選型的理解深度。今天我們就來(lái)徹底拆解這個(gè)問(wèn)題。我將分享兩種最核心、最實(shí)用的方法并深入探討它們背后的原理、適用場(chǎng)景以及那些新手極易踩坑的細(xì)節(jié)。無(wú)論你是正在刷題準(zhǔn)備面試的學(xué)生還是日常開(kāi)發(fā)中需要處理數(shù)據(jù)增刪的業(yè)務(wù)工程師掌握這兩種方法及其精髓都能讓你在面對(duì)“數(shù)組插入”時(shí)從“能實(shí)現(xiàn)”進(jìn)階到“實(shí)現(xiàn)得高效、優(yōu)雅”。2. 方法一手動(dòng)位移法——理解內(nèi)存操作的底層邏輯手動(dòng)位移法是最直接、最能體現(xiàn)數(shù)組底層特性的方法。它的核心思想是既然數(shù)組在內(nèi)存中是連續(xù)存儲(chǔ)的無(wú)法直接“撐開(kāi)”那么我們就手動(dòng)為新的元素騰出位置。2.1 核心思路與算法步驟這個(gè)過(guò)程非常像在圖書館一排擺滿書的書架中間插入一本新書。你不能直接把書“變”進(jìn)去而是需要先把目標(biāo)位置及后面的書都往后挪一格空出一個(gè)位置再把新書放進(jìn)去。具體到代碼邏輯可以分為以下清晰的三步檢查與準(zhǔn)備首先確認(rèn)數(shù)組是否有足夠的容量如果使用的是固定長(zhǎng)度數(shù)組如C/C的基礎(chǔ)數(shù)組或Java中已初始化的數(shù)組。然后明確你要插入的位置索引假設(shè)為pos和要插入的值假設(shè)為value。創(chuàng)造空間這是最關(guān)鍵的一步。我們需要將數(shù)組中從pos開(kāi)始到最后一個(gè)元素的所有元素都向后移動(dòng)一位。注意必須從最后一個(gè)元素開(kāi)始倒序向后移動(dòng)。如果從pos開(kāi)始正序移動(dòng)你會(huì)覆蓋掉pos1的元素然后這個(gè)被覆蓋的值再去覆蓋pos2導(dǎo)致數(shù)據(jù)丟失。插入元素在pos這個(gè)現(xiàn)在已空出的位置上放入我們的新值value。注意這里隱含了一個(gè)重要前提我們通常需要一個(gè)“邏輯長(zhǎng)度”變量來(lái)記錄數(shù)組當(dāng)前實(shí)際存了多少個(gè)元素而不是數(shù)組物理上分配的長(zhǎng)度。插入后這個(gè)邏輯長(zhǎng)度需要加1。2.2 代碼實(shí)現(xiàn)與逐行解析我們以Java語(yǔ)言為例假設(shè)我們管理著一個(gè)整型數(shù)組arr一個(gè)表示當(dāng)前元素?cái)?shù)量的size以及數(shù)組的總?cè)萘縞apacity。/** * 在指定位置插入一個(gè)元素手動(dòng)位移法 * param arr 目標(biāo)數(shù)組 * param size 數(shù)組當(dāng)前元素個(gè)數(shù)引用傳遞以便修改 * param capacity 數(shù)組總?cè)萘?* param pos 要插入的位置索引0-based * param value 要插入的值 * return 插入是否成功 */ public static boolean insertByShift(int[] arr, int[] size, int capacity, int pos, int value) { // 1. 邊界條件檢查 if (size[0] capacity) { System.out.println(插入失敗數(shù)組已滿。); return false; } if (pos 0 || pos size[0]) { // 允許在末尾插入(pos size[0]) System.out.println(插入失敗插入位置越界。); return false; } // 2. 從后向前移動(dòng)元素騰出pos位置 for (int i size[0] - 1; i pos; i--) { arr[i 1] arr[i]; // 將元素向后移動(dòng)一位 } // 3. 在空出的位置插入新元素 arr[pos] value; // 4. 更新數(shù)組當(dāng)前大小 size[0]; System.out.println(插入成功。); return true; }關(guān)鍵點(diǎn)解析size使用數(shù)組傳遞這是一個(gè)小技巧。因?yàn)镴ava是值傳遞為了在方法內(nèi)部修改外部的size變量我們將其包裹在一個(gè)單元素?cái)?shù)組中從而達(dá)到“引用”效果。在實(shí)際項(xiàng)目或其它語(yǔ)言如C中可能直接使用指針或引用。循環(huán)條件i pos這確保了位置pos的元素也會(huì)被移動(dòng)。當(dāng)i等于pos時(shí)執(zhí)行arr[pos1] arr[pos]這樣pos位置就空出來(lái)了。允許pos size[0]這意味著可以在當(dāng)前所有元素的末尾插入此時(shí)循環(huán)條件i pos因?yàn)閕初始為size[0]-1小于pos循環(huán)體不會(huì)執(zhí)行直接執(zhí)行插入和size增加邏輯完全正確。2.3 時(shí)間復(fù)雜度與空間復(fù)雜度分析這是評(píng)價(jià)算法性能的關(guān)鍵也是面試必問(wèn)點(diǎn)。時(shí)間復(fù)雜度O(n)。這里的n通常指數(shù)組中需要移動(dòng)的元素?cái)?shù)量在最壞情況下在數(shù)組頭部插入即pos0需要移動(dòng)所有size個(gè)元素。平均而言需要移動(dòng)size/2個(gè)元素但時(shí)間復(fù)雜度描述的是增長(zhǎng)趨勢(shì)所以仍然是 O(n)??臻g復(fù)雜度O(1)。我們只使用了固定的幾個(gè)額外變量i,pos,value等沒(méi)有使用隨數(shù)組規(guī)模增長(zhǎng)而增長(zhǎng)的額外存儲(chǔ)空間因此是常數(shù)復(fù)雜度。適用場(chǎng)景與心得 手動(dòng)位移法適用于所有需要顯式控制內(nèi)存和過(guò)程的場(chǎng)景特別是在嵌入式開(kāi)發(fā)、對(duì)性能有極致要求的底層系統(tǒng)、或者學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)的初期。它讓你清晰地感知到每一次數(shù)據(jù)操作的成本。我個(gè)人的體會(huì)是在面試中手寫這種方法能很好地展示你對(duì)基礎(chǔ)的理解。但在日常業(yè)務(wù)開(kāi)發(fā)中如果語(yǔ)言提供了更高級(jí)的抽象我們通常會(huì)選擇更簡(jiǎn)潔的方法二。3. 方法二使用標(biāo)準(zhǔn)庫(kù)函數(shù)——站在巨人的肩膀上對(duì)于大多數(shù)現(xiàn)代高級(jí)編程語(yǔ)言如Python, Java, JavaScript等其標(biāo)準(zhǔn)庫(kù)或內(nèi)置類型已經(jīng)為我們封裝了高效且穩(wěn)健的數(shù)組插入操作。這種方法的核心思想是避免重復(fù)造輪子利用語(yǔ)言或框架提供的、經(jīng)過(guò)充分優(yōu)化的工具。3.1 不同語(yǔ)言下的實(shí)現(xiàn)范例不同語(yǔ)言對(duì)此的支持程度和語(yǔ)法各不相同但理念相通。Python使用list.insert()Python的列表list是動(dòng)態(tài)數(shù)組其insert()方法完美封裝了插入操作。my_list [1, 2, 3, 5] # 在索引2即第三個(gè)位置插入元素4 my_list.insert(2, 4) print(my_list) # 輸出: [1, 2, 4, 3, 5]Python的list.insert(pos, value)在內(nèi)部自動(dòng)處理了所有邊界檢查、內(nèi)存重分配如果需要擴(kuò)容和元素位移我們只需一行代碼。Java使用ArrayList.add()在Java中我們通常使用ArrayList這個(gè)動(dòng)態(tài)數(shù)組類來(lái)代替基礎(chǔ)數(shù)組。import java.util.ArrayList; ArrayListInteger list new ArrayList(Arrays.asList(1, 2, 3, 5)); // 在索引2處插入元素4 list.add(2, 4); System.out.println(list); // 輸出: [1, 2, 4, 3, 5]ArrayList.add(index, element)方法同樣封裝了所有細(xì)節(jié)。需要注意的是ArrayList在底層也是數(shù)組其add(index, e)方法的時(shí)間復(fù)雜度依然是O(n)因?yàn)樗鼉?nèi)部也需要移動(dòng)元素。但它的優(yōu)勢(shì)在于自動(dòng)擴(kuò)容、豐富的API和更好的集成性。JavaScript使用Array.splice()JavaScript數(shù)組的splice()方法功能非常強(qiáng)大可以同時(shí)實(shí)現(xiàn)插入、刪除和替換。let myArray [1, 2, 3, 5]; // 在索引2處刪除0個(gè)元素插入元素4 myArray.splice(2, 0, 4); console.log(myArray); // 輸出: [1, 2, 4, 3, 5]splice(start, deleteCount, item1, item2, ...)的語(yǔ)義是從start索引開(kāi)始刪除deleteCount個(gè)元素然后插入后續(xù)的所有參數(shù)。這里deleteCount為0所以是純插入。3.2 方法二的底層原理與性能雖然我們調(diào)用的是高級(jí)API但了解其底層原理至關(guān)重要這能避免我們誤用。無(wú)論是Python的list、Java的ArrayList還是JavaScript的Array它們?cè)诘讓哟鎯?chǔ)數(shù)據(jù)時(shí)本質(zhì)上仍然是基于一塊連續(xù)的內(nèi)存空間數(shù)組。當(dāng)你調(diào)用insert,add(index,e)或splice進(jìn)行插入時(shí)解釋器或虛擬機(jī)在內(nèi)部執(zhí)行的邏輯與我們手動(dòng)編寫的“位移法”在核心步驟上是一致的檢查邊界和容量。如果需要進(jìn)行動(dòng)態(tài)擴(kuò)容例如申請(qǐng)一塊更大的內(nèi)存拷貝舊數(shù)據(jù)。將插入點(diǎn)之后的元素向后移動(dòng)。放入新元素。更新內(nèi)部的長(zhǎng)度記錄。因此這些高級(jí)API在中間位置插入的時(shí)間復(fù)雜度平均和最壞情況下仍然是O(n)。它們并沒(méi)有魔法只是把復(fù)雜且易錯(cuò)的細(xì)節(jié)隱藏了起來(lái)提供了更安全、更便捷的接口。它們的優(yōu)勢(shì)在于代碼簡(jiǎn)潔一行代碼代替多行。健壯性強(qiáng)內(nèi)置了邊界檢查、類型檢查在強(qiáng)類型語(yǔ)言中和自動(dòng)擴(kuò)容。經(jīng)過(guò)優(yōu)化標(biāo)準(zhǔn)庫(kù)的實(shí)現(xiàn)往往由專家編寫并針對(duì)特定語(yǔ)言運(yùn)行時(shí)進(jìn)行過(guò)深度優(yōu)化可能比我們自己寫的樸素版本效率更高。3.3 如何選擇手動(dòng)法 vs 庫(kù)函數(shù)法這是一個(gè)典型的“造輪子”與“用輪子”的選擇題。我的經(jīng)驗(yàn)法則是首選庫(kù)函數(shù)在99%的業(yè)務(wù)開(kāi)發(fā)、算法題允許使用標(biāo)準(zhǔn)庫(kù)時(shí)和腳本編寫場(chǎng)景中毫不猶豫地使用語(yǔ)言提供的標(biāo)準(zhǔn)庫(kù)函數(shù)。它的目的是提升開(kāi)發(fā)效率、減少錯(cuò)誤并且其性能在絕大多數(shù)情況下都是完全可接受的。使用手動(dòng)法的場(chǎng)景學(xué)習(xí)與教學(xué)為了深入理解數(shù)組和數(shù)據(jù)結(jié)構(gòu)的原理。面試特定要求有些面試官明確要求不能使用高級(jí)API以考察基本功。極端性能優(yōu)化在極其特殊的性能敏感場(chǎng)景你可能有自定義的內(nèi)存布局或優(yōu)化策略需要精細(xì)控制每一次拷貝。但這屬于非常高級(jí)的優(yōu)化需要充分的性能剖析數(shù)據(jù)支撐。底層或嵌入式開(kāi)發(fā)所在的環(huán)境可能沒(méi)有提供這樣的高級(jí)容器庫(kù)。實(shí)操心得不要陷入“高級(jí)API性能一定差”的誤區(qū)?,F(xiàn)代語(yǔ)言的標(biāo)準(zhǔn)庫(kù)實(shí)現(xiàn)極其高效。我曾見(jiàn)過(guò)有人為了“優(yōu)化”而自己實(shí)現(xiàn)一個(gè)動(dòng)態(tài)數(shù)組結(jié)果不僅引入了bug性能還不如直接使用ArrayList。在優(yōu)化之前先進(jìn)行測(cè)量Profiling。4. 插入操作的邊界情況與陷阱防范無(wú)論是手動(dòng)實(shí)現(xiàn)還是調(diào)用庫(kù)函數(shù)處理邊界情況都是保證程序健壯性的關(guān)鍵。以下是幾個(gè)最常見(jiàn)的陷阱及其防范措施。4.1 插入位置越界這是最經(jīng)典的錯(cuò)誤。插入位置pos的有效范圍通常是[0, current_size]。注意current_size是允許的表示在末尾追加。錯(cuò)誤示例pos 0或pos current_size嚴(yán)格大于。防范在操作前必須進(jìn)行校驗(yàn)。// 手動(dòng)實(shí)現(xiàn)時(shí)的檢查 if (pos 0 || pos size) { throw new IndexOutOfBoundsException(插入位置: pos , 數(shù)組大小: size); }庫(kù)函數(shù)行為像ArrayList.add(index, e)這樣的方法如果索引越界會(huì)拋出IndexOutOfBoundsException。這是我們需要捕獲和處理的異常。4.2 數(shù)組容量不足對(duì)于固定長(zhǎng)度的基礎(chǔ)數(shù)組如果當(dāng)前元素?cái)?shù)量size已經(jīng)等于數(shù)組長(zhǎng)度capacity則無(wú)法再插入。防范動(dòng)態(tài)擴(kuò)容這是高級(jí)容器如ArrayList的做法。當(dāng)容量不足時(shí)申請(qǐng)一個(gè)更大的新數(shù)組通常是原容量的1.5或2倍將舊數(shù)據(jù)拷貝過(guò)去然后繼續(xù)操作。手動(dòng)實(shí)現(xiàn)可以參考此邏輯。提前檢查在插入前檢查if (size capacity)如果已滿則返回錯(cuò)誤或觸發(fā)擴(kuò)容流程。庫(kù)函數(shù)行為Python list、Java ArrayList等都會(huì)自動(dòng)處理擴(kuò)容用戶通常無(wú)需關(guān)心。4.3 在遍歷過(guò)程中修改數(shù)組這是一個(gè)非常隱蔽的陷阱。當(dāng)你使用for循環(huán)或迭代器遍歷數(shù)組/列表時(shí)如果直接在遍歷過(guò)程中進(jìn)行插入或刪除很容易導(dǎo)致循環(huán)變量錯(cuò)亂或并發(fā)修改異常。錯(cuò)誤示例ArrayListInteger list new ArrayList(Arrays.asList(1, 2, 3, 4)); for (int i 0; i list.size(); i) { if (list.get(i) 2) { list.add(i, 99); // 插入操作改變了list.size()和后續(xù)元素的索引 // 這可能導(dǎo)致死循環(huán)或漏掉某些元素的檢查 } }正確做法倒序遍歷如果需要在遍歷時(shí)插入且插入位置不影響已遍歷的部分可以考慮從后向前遍歷。收集操作最后執(zhí)行先遍歷記錄下需要插入的位置和值存到另一個(gè)列表中。遍歷結(jié)束后再統(tǒng)一執(zhí)行插入操作注意從后往前插入避免影響之前記錄的位置。使用迭代器如果支持某些集合類的迭代器提供了安全的add方法。4.4 特殊位置插入的細(xì)節(jié)頭部插入 (pos 0)這是最耗時(shí)的操作需要移動(dòng)所有元素。如果頻繁在頭部插入應(yīng)考慮使用鏈表LinkedList這種數(shù)據(jù)結(jié)構(gòu)其在頭部插入的時(shí)間復(fù)雜度是O(1)。尾部插入 (pos size)這是最高效的插入操作通常不需要移動(dòng)任何元素除非觸發(fā)擴(kuò)容時(shí)間復(fù)雜度可視為O(1)攤銷常數(shù)時(shí)間。因此如果業(yè)務(wù)場(chǎng)景允許盡量采用追加的方式。5. 性能優(yōu)化與高級(jí)技巧探討當(dāng)我們對(duì)插入性能有更高要求時(shí)就需要跳出“每次插入都移動(dòng)元素”的思維定式。5.1 批量插入的優(yōu)化策略如果需要連續(xù)插入多個(gè)元素最差的做法是循環(huán)調(diào)用單次插入API。低效做法O(k * n)k為插入次數(shù)n為數(shù)組大小。優(yōu)化思路計(jì)算總位移先確定所有元素插入后最終需要移動(dòng)的“大區(qū)塊”。一次性移動(dòng)只執(zhí)行一次大規(guī)模的元素向后移動(dòng)。填充新數(shù)據(jù)將待插入的多個(gè)元素一次性放入空出的位置。例如在數(shù)組[A, B, C, D]的位置1后連續(xù)插入[X, Y, Z]。 低效做法插X移一次插Y再移一次此時(shí)X也被移動(dòng)了插Z再移一次。 高效做法計(jì)算出最終需要為[X,Y,Z]空出3個(gè)位置直接將[B,C,D]一次性向后移動(dòng)3格然后將X,Y,Z填入空位。很多標(biāo)準(zhǔn)庫(kù)的批量插入方法如Python list的切片賦值list[1:1] [X, Y, Z]在內(nèi)部就采用了類似的優(yōu)化。5.2 數(shù)據(jù)結(jié)構(gòu)選型何時(shí)放棄數(shù)組這是從根本上解決插入性能問(wèn)題的思路。數(shù)組的連續(xù)內(nèi)存特性決定了其中間插入的成本。如果你的應(yīng)用場(chǎng)景頻繁在任意位置進(jìn)行插入或刪除那么數(shù)組或基于數(shù)組的ArrayList可能不是最佳選擇。鏈表LinkedList鏈表在已知節(jié)點(diǎn)位置的情況下插入和刪除操作的時(shí)間復(fù)雜度是O(1)因?yàn)樗恍枰薷闹羔樁恍枰苿?dòng)大量數(shù)據(jù)。代價(jià)是隨機(jī)訪問(wèn)元素變慢O(n)。平衡搜索樹(shù)如TreeSet/TreeMap或跳表它們能保持元素有序并且插入、刪除、查找的時(shí)間復(fù)雜度都是O(log n)是一個(gè)在有序性和操作效率之間很好的折中。哈希表HashSet/HashMap如果你不關(guān)心順序只關(guān)心快速判斷存在性和插入哈希表的平均插入時(shí)間復(fù)雜度是O(1)。選型決策框架訪問(wèn)模式是隨機(jī)訪問(wèn)多按索引還是順序訪問(wèn)多修改模式插入/刪除是主要在尾部還是在中間/頭部頻率如何是否需有序數(shù)據(jù)是否需要保持插入順序或某種排序例如實(shí)現(xiàn)一個(gè)“最近使用的文件”列表尾部插入和頭部刪除很頻繁中間操作少那么使用一個(gè)定長(zhǎng)的隊(duì)列可以用循環(huán)數(shù)組實(shí)現(xiàn)可能比鏈表更高效因?yàn)閿?shù)組的緩存局部性更好。5.3 空間換時(shí)間的預(yù)處理思想在某些特定場(chǎng)景下我們可以通過(guò)額外的空間來(lái)提升插入效率。預(yù)留空位Padding如果知道大概的插入頻率可以初始化一個(gè)比實(shí)際需要更大的數(shù)組并在數(shù)據(jù)間預(yù)留一些空位。插入時(shí)可能只需要移動(dòng)附近一小部分?jǐn)?shù)據(jù)甚至不需要移動(dòng)如果插入點(diǎn)正好是預(yù)留空位。這類似于數(shù)據(jù)庫(kù)的頁(yè)填充因子Fill Factor概念。缺點(diǎn)是浪費(fèi)空間且空位用完后性能會(huì)退化。分塊數(shù)組Blocked Array將一個(gè)大數(shù)組分成許多小塊。插入時(shí)只影響其中一個(gè)塊移動(dòng)的數(shù)據(jù)量就限制在塊大小內(nèi)。結(jié)合鏈表管理這些塊可以在O(√n)或更優(yōu)的時(shí)間內(nèi)完成插入。這是一種更復(fù)雜但平衡了數(shù)組和鏈表優(yōu)點(diǎn)的數(shù)據(jù)結(jié)構(gòu)在某些數(shù)據(jù)庫(kù)和文本編輯器中有應(yīng)用。6. 實(shí)戰(zhàn)手寫一個(gè)簡(jiǎn)易的動(dòng)態(tài)數(shù)組ArrayList為了融會(huì)貫通我們嘗試手動(dòng)實(shí)現(xiàn)一個(gè)簡(jiǎn)化版的動(dòng)態(tài)數(shù)組支持自動(dòng)擴(kuò)容和在任意位置插入。這能讓你徹底理解ArrayList等容器類的工作原理。public class SimpleDynamicArray { private int[] data; // 內(nèi)部存儲(chǔ)數(shù)組 private int size; // 當(dāng)前元素?cái)?shù)量 private int capacity; // 數(shù)組總?cè)萘?// 構(gòu)造函數(shù)初始化容量 public SimpleDynamicArray(int initialCapacity) { if (initialCapacity 0) { throw new IllegalArgumentException(初始容量必須大于0); } this.capacity initialCapacity; this.data new int[initialCapacity]; this.size 0; } // 在指定索引插入元素 public void insert(int index, int value) { // 1. 邊界檢查 if (index 0 || index size) { throw new IndexOutOfBoundsException(索引: index , 大小: size); } // 2. 容量檢查與擴(kuò)容 if (size capacity) { resize(capacity * 2); // 常見(jiàn)的擴(kuò)容策略翻倍 } // 3. 從后向前移動(dòng)元素 for (int i size - 1; i index; i--) { data[i 1] data[i]; } // 4. 插入新元素 data[index] value; // 5. 更新大小 size; } // 擴(kuò)容方法 private void resize(int newCapacity) { int[] newData new int[newCapacity]; // 拷貝舊數(shù)據(jù) for (int i 0; i size; i) { newData[i] data[i]; } data newData; capacity newCapacity; System.out.println(數(shù)組已擴(kuò)容至: newCapacity); } // 其他輔助方法獲取大小、根據(jù)索引獲取值等... public int getSize() { return size; } public int get(int index) { if (index 0 || index size) throw new IndexOutOfBoundsException(); return data[index]; } }實(shí)現(xiàn)要點(diǎn)解析封裝將內(nèi)部數(shù)組data、當(dāng)前大小size和容量capacity封裝在類內(nèi)部對(duì)外提供安全的insert和get接口。自動(dòng)擴(kuò)容insert方法在檢測(cè)到size capacity時(shí)調(diào)用私有的resize方法。常見(jiàn)的擴(kuò)容因子是2或1.5這樣能保證多次插入的攤銷時(shí)間復(fù)雜度仍為O(1)。攤銷分析的意思是雖然單次擴(kuò)容是O(n)的但平攤到后續(xù)的n次插入上每次的成本是常數(shù)。異常處理對(duì)索引進(jìn)行了嚴(yán)格的檢查并拋出標(biāo)準(zhǔn)異常使類的行為更符合Java慣例。通過(guò)這個(gè)練習(xí)你會(huì)對(duì)“動(dòng)態(tài)數(shù)組”如何工作、擴(kuò)容的成本與收益、以及封裝的重要性有更深刻的認(rèn)識(shí)。在實(shí)際開(kāi)發(fā)中我們當(dāng)然直接使用java.util.ArrayList但了解其原理能讓你在使用時(shí)更加自信在遇到性能問(wèn)題時(shí)也能有的放矢地進(jìn)行排查和優(yōu)化。數(shù)組插入這個(gè)基礎(chǔ)操作串聯(lián)起了數(shù)據(jù)結(jié)構(gòu)、算法復(fù)雜度、API設(shè)計(jì)和性能優(yōu)化的多個(gè)核心知識(shí)點(diǎn)值得每一個(gè)開(kāi)發(fā)者深入掌握。