2. 數組:爲什麼很多編程語⾔中數組都從0開始編號

數組(Array) 是⼀種線性表數據結構。 它⽤⼀組連續的內存空間, 來存儲⼀組具有相同類型的數據。
線性表(Linear List):線性表就是數據排成像⼀條線⼀樣的結構。 每個線性表上的數據最多隻有前和後兩個⽅向。 其實除了數組, 鏈表、 隊列、 棧等也是線性表結構。
⾮線性表, ⽐如⼆叉樹、 堆、 圖等。 之所以叫⾮線性, 是因爲, 在⾮線性表中, 數據之間並不是簡單的前後關係。
連續的內存空間和相同類型的數據。 正是因爲這兩個限制, 它纔有了⼀個堪稱“殺⼿鐗”的特性: “隨機訪問”。 但有利就有弊, 這兩個限制也讓數組的很多操作變得⾮常低效, ⽐如要想在數組中刪除、 插⼊⼀個數據, 爲了保證連續性, 就需要做⼤量的數據搬移⼯作。

我們知道, 計算機會給每個內存單元分配⼀個地址, 計算機通過地址來訪問內存中的數據。 當計算機需要隨機訪問數組中的某個元素時, 它會⾸先通過下⾯的尋址公式, 計算出該元素存儲的內存地址:


其中data_type_size表示數組中每個元素的⼤⼩。 我們舉的這個例⼦⾥, 數組中存儲的是int類型數據, 所以data_type_size爲4個字節。從數組存儲的內存模型上來看, “下標”最確切的定義應該是“偏移(offset) ”。 前⾯也講到, 如果⽤a來表示數組的⾸地址, a[0]就是偏移爲0的位置, 也就是⾸地址, a[k]就表示偏移k個type_size的位置。

數組和鏈表的區別, 很多⼈都回答說, “鏈表適合插⼊、 刪除,時間複雜度O(1); 數組適合查找, 查找時間複雜度爲O(1)”。實際上, 這種表述是不準確的。 數組是適合查找操作, 但是查找的時間複雜度並不爲O(1)。 即便是排好序的數組, 你⽤⼆分找, 時間複雜度也是O(logn)。 所以, 正確的表述應該是, 數組⽀持隨機訪問, 根據下標隨機訪問的時間複雜度爲O(1)。


ArrayList最⼤的優勢就是可以將很多數組操作的細節封裝起來。 ⽐如前⾯提到的數組插⼊、 刪除數據時需要搬移其他數據等。 另外, 它還有⼀個優勢, 就是⽀持動態擴容。
數組本身在定義的時候需要預先指定⼤⼩, 因爲需要分配連續的內存空間。 如果我們申請了⼤⼩爲10的數組, 當第11個數據需要存儲到數組中時, 我們就需要重新分配⼀塊更⼤的空間, 將原來的數據複製過去, 然後再將新的數據插⼊。
如果使⽤ArrayList, 我們就完全不需要關⼼底層的擴容邏輯, ArrayList已經幫我們實現好了。 每次存儲空間不夠的時候, 它都會將空間⾃動擴容爲1.5倍⼤⼩。
不過, 這⾥需要注意⼀點, 因爲擴容操作涉及內存申請和數據搬移, 是⽐較耗時的。 所以, 如果事先能確定需要存儲的數據⼤⼩, 最好在創建ArrayList的時候事先指定數據⼤⼩。作爲⾼級語⾔編程者, 是不是數組就⽆⽤武之地了呢? 當然不是, 有些時候, ⽤數組會更合適些, 我總結了⼏點⾃⼰的經驗。
1.Java ArrayList⽆法存儲基本類型, ⽐如int、 long, 需要封裝爲Integer、 Long類, ⽽Autoboxing、 Unboxing則有⼀定的性能消耗, 所以如果特別關注性能, 或者希望使⽤基本類型, 就可以選⽤數組。
2.如果數據⼤⼩事先已知, 並且對數據的操作⾮常簡單, ⽤不到ArrayList提供的⼤部分⽅法, 也可以直接使⽤數組。
3.還有⼀個是我個⼈的喜好, 當要表示多維數組時, ⽤數組往往會更加直觀。 ⽐如Object[][] array; ⽽⽤容器的話則需要這樣定義: ArrayList<ArrayList > array。
 對於業務開發, 直接使⽤容器就⾜夠了, 省時省⼒。 畢竟損耗⼀丟丟性能, 完全不會影響到系統整體的性能。 但如果你是做⼀些⾮常底層的開發, ⽐如開發⽹絡框架, 性能的優化需要做到極致, 這個時候數組就會優於容器, 成爲⾸選。

 

 

 


 


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章