來吧!給你不一樣的數組深入講解

數組小談

慶哥: 嗨,小白,知道啥是數組嗎?小白: 你看你這話說的,數組那還不簡單,學計算機的沒有不知道數組的吧,我們剛開始接觸C語言的時候就有數組啊,現在在學習java,也有數組啊,一般不就這樣嘛

int[] array = new int[10]

這就創建了一個長度為10的數組,是不是?

慶哥: 嗯嗯,你還知道數組的啥啊。比如特性啥的?

小白: 這個嘛,數組啊,最經典的不就是可以根據數組下標來讀取數據嗎?比如上面定義的那個長度為10的數組,可以使用array[1]來獲得數組第二個位置的數據。

慶哥: 為啥不是第一個的數據,那不是1嗎?

小白: 這個嘛,那是因為數組下標是從0開始的,所以實際上1就是2的位置,就像這樣:

來吧!給你不一樣的數組深入講解

慶哥: 那數組下標為啥從0開始啊

小白: 嗯。。。這個?不知道

慶哥: 在Java中數組其實可以看做是一個對象嘛?

小白: 納尼?沒有考慮過這個問題啊

慶哥: 知道Array和Arrays嘛

小白: 不是太清楚

慶哥: 哈哈,來吧,今天就讓你徹底搞懂數組這傢伙

小白: 小板凳已備好

啥是數組啊

天馬星空看數組

慶哥: 既然我們要學習數組,那首要的任務就是先要弄懂,啥是數組?你現在清空大腦,不要想著什麼數組是數據結構,什麼連續內存,什麼隨機訪問,什麼不可變的,拋出你之前關於數組的所有認知,從零開始,你想想,數組是個啥?

小白: 那我要發揮我超強的想象力了哦,數組嘛,單從這倆字啊,我想著這玩意應該是個跟棍似的,一組一組的嘛,長長的,然後是數據嘛,人家是數組,那應該就是跟數據有關的,組?我們平常會說一組一組的,比如幾人一組,那這個數組就是一些數據在一塊。。。。我在說啥

慶哥: 可以啊,你這想象力,那我來說下我的理解吧,初看數組這貨,我就覺得啊,它在java中應該也是個類,然後我們也可以通過最基本的new來創建一個數組,剛開始就這樣感覺得,但是誰知道這傢伙完全不按照套路出牌啊

小白: 你這樣一說也真是的,我本來也以為數組可以使用new嘞,有的時候還是有點疑惑的,特別是遇到Array這個類就更迷惑了

慶哥: 的確,我剛開始也是這樣,覺得這個Array不就是數組嘛,然後我還這樣試過

來吧!給你不一樣的數組深入講解

哈哈,我當時還迷惑,這是咋回事啊,這不是創建數組嘛

小白: 這是咋回事啊,為啥會報錯啊

慶哥: 這個嘛,我們看看Array的源碼就知道了

來吧!給你不一樣的數組深入講解

看到了吧,它的構造方法是私有的,所以是不能被實例化的,因為我們使用new就是通過構造方法去創建啊,而這裡是私有的,所以是不行滴,知道了吧

小白: 嗯嗯,看來我對這個數組還真的是不瞭解啊,慶哥這次要好好和我說說

java中的數組

慶哥: 沒問題,那我們言歸正傳,看看java中的數組到底是個啥,在java中啊,我們可以這樣理解數組:

數組這傢伙就是一個數據集合,而且它還是有序的,就是很多數據擱在一塊,一個挨著一個,而且每個數據的類型都是相同的

這裡簡單大白話說了啥是數組,有必要還是畫個圖,來加深下印象,對了對了,之前畫了個圖,就用這個

來吧!給你不一樣的數組深入講解

好了,現在對於數組,給你文字說了是啥,現在再看個圖,這裡就相當於有一個數組,這個數組有10個數據,你看他們是不是一個挨著一個,然後每個數據還有一個下標,這個下標就相當於給每個數據進行編號,你想啊,編號有啥好處?

是不是更加容易尋找了,對號入座嘛!所以這裡就引出了數組的一個大大的特點:

人家支持隨機訪問啊

小白: 這個是不是就是可以使用下標訪問數據的特性

慶哥: 嗯嗯,是的,我們接著來說,我們現在對數組有個大致印象, ,要知道數組最終是用來幹嘛的。人家是用來存放數據的,我們還是先來看看數組的的相關代碼吧:

int[] a = new int[10];

看,這就創建了一個數組,不是我們想的使用new的方式,其實從代碼我們就能猜到,這個數組的長度是10,啥意思,數組人家是有長度的。

小白: 這個是必須要給它指定一個長度吧

慶哥: 這個就牽涉到數組的聲明瞭,數組是用來存放數據的,那我們要用它的話就要先創造它吧,之前說了,人家不是使用new,人家是這樣的形式

int[] a = new int[10];

那麼這樣可以嗎?

int[] a = new int[];

有啥區別?是不是少了個10,行嗎這樣,我們看看

來吧!給你不一樣的數組深入講解

看到了,不行,為啥不行啊,你想想?

小白: 這個嘛,這裡的10是在給數組一個指定的長度吧,難道不能默認長度為0嗎?

慶哥: 這裡就要看數組的一些特點了,對於數組啊,人家是有序數據的集合,在內存中來展示就是人家需要連續的內存空間,還有一個特點那就是你一旦聲明就不會再改變了

小白: 嗯嗯,這個我知道,數組一旦確定將是固定不變的,哦哦,我知道了,那這裡就必須要求指定長度了,不然相當於沒創建數組啊,長度為0沒意義啊

數組在內存中的分配

慶哥: 對的,我們再看這段代碼

int[] a = new int[10];

是個怎麼回事,首先我們知道了,這是要創建數組吧,長度也指定了,是10,對了這裡忘了說了,這個數組是個整型數組,類型是int,為啥要說這個嘞,等會再詳細說,先來看圖

來吧!給你不一樣的數組深入講解

啥意思嘞?這些個格格就可以看成是內存,一塊塊的內存,現在我們創建一個長度為10的整型數組,那就需要十塊這樣的內存空間。

小白: 這裡紅色的塊是啥意思啊?

慶哥: 紅色啊,就代表已經被使用了的內存空間,看出啥問題了沒,現在你創建這樣的代碼

int[] a = new int[10];

意思就是你告訴內存,“內存那傢伙,給我來十塊內存空間,記住必須連續的哦”,也就是說,數組申請的內存空間是連續分配的,數組中的數據比較矯情,必須一個挨著一個

簡單聊聊初始化

小白: 嗯嗯,我記得數組的聲明不止這一種吧?

慶哥: 是的,我們之前介紹的這樣叫做動態初始化數組,啥意思嘞,就是指定了長度,但是沒有給值,我們看這個

int[] a = new int[10];
System.out.println(a[2]);

輸出是0,你可以試下,這10個數據都是0,這是默認初始化的值,我們也可以使用這樣靜態初始化

int[] b = new int[]{1, 2, 3};

啥意思,也就是在創建數組的時候就把值給確定下來,你猜猜這樣創建的數組長度是多少?

小白: 這個啊,這裡沒有給定像10那樣的數據啊,這個應該是。。。3吧,後面大括號有三個數

慶哥: 我們看下這段代碼

int[] b = new int[]{1, 2, 3};

System.out.println(b.length);

這是啥,這是求數組長度的,我們看看輸出

來吧!給你不一樣的數組深入講解

被你說對了,像這樣的,它會根據你大括號裡的數據創建內存空間,也就是說有幾個數就申請幾個內存,不多不少申請了就固定了

數組變量和數組對象

慶哥: 我們上面已經簡單介紹完數組了,那你知道什麼是數組變量什麼是數組對象嗎

小白: 我去,暈菜,這倆雙胞胎吧

慶哥: 那我來說說,啥是數組變量,看之前的代碼

int[] a = new int[10]

這個a其實就是一個數組變量,一個數組變量指向一個數組對象,數組變量其實就是一個引用,存放的是內存地址,也就是引用是與內存地址劃等號的,內存地址是給計算機看的,而引用是給我們看的(一個我們比較熟悉的符號),而這個a其實是數組的首地址,指向的數組對象其實就是a[0]這個數組變量對應的數組對象。

那數組對象其實就是內存空間中存放的數值了。

數組初始化是對誰初始化

那我們接著這個數組變量和數組對象來講講,數組初始化是對誰進行初始化,其實也很簡單,知道了什麼是數組變量和數組對象之後,我們就應該知道,數組初始化不是對數組變量初始化,而是對數組對象進行初始化。

所謂的對數組對象初始化,無非就是告訴內存,我要創建一個數組,你得給我分配一塊連續的內存。

咋樣,明白了吧

小白: ok的

數組能存儲啥

慶哥: 好了,以上屬於數組的基本知識,不多說了,我們接下來再來說說數組的數據存儲類型,還記得之前說的嗎?數組中的每個元素都是相同的類型,我們上面是int類型的,啥意思嘞?也就是說啊,你創建什麼類型的數組就只能存放什麼類型的數據。

比如這樣就不行

來吧!給你不一樣的數組深入講解

因為你創建了是個int整型的,所以只能存放整型數據,字符串就不行,想要存放字符串的話那就要創建一個字符串數組

String[] strings = new String[]{"hello"};

有個小例外

這裡啊,是有個例外的 ,我們看這個:

Object[] objects = new Object[10];
objects[0] = 1;
objects[1] = "hello";

這個是都可以的,知道為啥不

小白: 這個啊,知道,Object可是java中所有類的超類啊

慶哥: 正解,好了,咱來簡單總結下:

Java中的數組是用來存儲同一種數據類型的數據結構,一旦初始化完成,在內存中的空間就已經固定了下來

數組是個對象?

慶哥: 你說數組是不是個對象?

小白: 數組是對象?不是說數組不能用new的方式創建嗎,那應該不是吧,感覺和平常的對象不一樣啊

慶哥: 實話告訴你吧,數組啊,其實是個特殊的對象,只不過數組比較特殊,它不同於一般的對象,像一般的對象都有特定的java類,比如我這裡創建了一個Person類,然後有這個代碼:

Person person = new Person();
System.out.println(person.getClass().getName());

就是創建一個Person對象,然後得到它的類名,如下

來吧!給你不一樣的數組深入講解

再來看個

Object object = new Object();
System.out.println(object.getClass().getName());

這個知道吧,它的類名一定是Object,我們看

來吧!給你不一樣的數組深入講解

是吧,我們再來看數組的,它可以這樣獲得

//數組是特殊的對象,沒有對應的類文件,數組類是在運行時產生的,類名很奇怪
System.out.println(new int[2].getClass().getName());

它這個類是啥嘞

來吧!給你不一樣的數組深入講解

這是啥玩意

小白: 我去,這裡我是不是隻要記住,其實數組也是個特殊的對象就行了吧、

慶哥: 對的,只需要記住,數組其實也是個對象就行,這可是對數組的深層次理解哦,那接下來我們繼續說數組的兩個讓人懵圈的類。

Array和Arrays

對這倆貨熟悉嗎

小白: 很懵圈啊,之前我還想使用Array來創建數組嘞

慶哥: 哈哈,使用它創建數組是不行的,話說這倆貨是幹嘛的啊,太容易讓人迷惑了,實際上啊,這倆貨的存在都是為了讓我們更一步的操作數組的,先來個簡單的例子,比如我們創建一個數組

int[] b = new int[]{1, 2, 3};
System.out.println(Array.get(b, 1));

看到沒,我們平常是使用b[1]來獲取下標為1的元素,使用Array就可以像上面那樣獲取,也就是說Array給我們提供了可以直接操作數組的一些方法,有如下這些:

來吧!給你不一樣的數組深入講解

小白: 這就是個類似工具類啊,可以方便我們操作數組是吧

慶哥: 對的,還記得我們之前就說過這個Array嗎,它的構造方法是私有的,不能實例化,它的方法也都是靜態的,可以直接還用類名來調用,就像上面那樣,就是為了我們更加方便的操作數組而存在的。

小白: 這樣說的話,那這個Arrays是不是類似啊

慶哥: 是的,Arrays也是個方便我們操作數組的工具類,只是他們提供的功能有所不同,我們使用Arrays可以實現給數組填充數據,也就是賦值,也可以進行排序啊,進行二分查找啊,截取數組啊之類的

小白: 這個提供的功能還不少嘞,使用上是不是和Array類似啊

慶哥: 是的,你看,比如說給數組數據進行排序

System.out.println("排序:");

int[] array1 = new int[]{5, 7, 8, 9, 1, 3, 6};
Arrays.sort(array1);

看它提供的方法

來吧!給你不一樣的數組深入講解

也有很多,需要啥我們用啥就行了

咋樣,這樣一介紹,是不是發現,其實這倆貨也沒啥難的啊、

小白: 是啊,原來就是兩個打輔助的啊

數組的特點

慶哥: 到了這裡,你是不是能 總結下數組的一些簡單特點啦

小白: 嗯嗯,數組啊,連續內存分配,可以隨機訪問,這次剛剛學到的,數組其實是個對象,對了,數組還有下標,是從0開始的。

慶哥: 嗯嗯,問題來了,為啥要從0開始,你知道嗎?

下標索引為啥從0開始

小白: 這個還真不知道,平常就記著下標是從0開始的了,至於為啥是從0開始還真不知道嘞,為啥啊慶哥。

慶哥: 要弄明白這個問題,要看這個圖

來吧!給你不一樣的數組深入講解

我們已經知道了,數組的創建需要連續的內存空間,比如這裡的整型數組,長度為10,那就再內存開闢了相應的內存空間,在內存空間中,每個內存是有相應的地址的,這個是操作系統乾的事,當你申請了內存之後,操作系統會為這塊內存地址進行編號。

比如這裡申請了一個長度為10的整型數組,我們知道int在java中佔4個字節,也就是說一個數據佔四個字節的內存,也就是上面的一塊.

然後每一塊都有相應的內存地址編號,比如0對應的這塊,編號是1000-1003,佔四個字節嘛,我們接下來結合數組的隨機訪問,比如我們使用array[0]去訪問第一個元素,實際指向的就是1000這個內存地址,這個在數組中也叫作首地址,數組變量array就是指向這個首地址。

這個首地址也叫作base_address,記住這個,然後我們如果要訪問下標為1的怎麼訪問嘞,這裡有個尋址公式

array[i] = base_address + i*data_type_size

base_address我們知道是啥了,那data_type_size是啥?其實即使數據類型的字節長度,比如這裡的int整型就是4個字節,那data_type_size就是4,我們現在要訪問下標為1的數據,代入公式就是:

array[1] = 1000 + 1*4 = 1004

看下,是不是剛好定位到下標為1的那塊內存的位置。

你想想,如果索引從1開始,那尋址供視是不是就變成了

array[i] = base_address + (i-1)*data_type_size

這就要多一步操作,浪費性能啊

咋樣明白不

小白: 嗯嗯,原來是這麼回事,終於學會了

數組的增刪改查

慶哥: 嗯嗯,那我們再來看看數組的增刪改查,也就是對數組進行的一些基本操作,首首先看看數組增加,我們還看著這個圖來說

來吧!給你不一樣的數組深入講解

如果我們要增加一個元素的話,怎麼辦?

小白: 這分幾種情況吧,插在最前面,中間和最後。

慶哥: 如果是插在中間呢?發現啥問題沒?

小白: 嗯。。我想想。。數組內存是連續分配的,那要插入一個新的元素,那豈不是要把當前位置的元素以及後面的元素全部後移了,這樣才能騰出位置插入當前元素吧

慶哥: 對的,所以對於數組,不僅是插入,刪除也是同樣的道理,如果不是尾部操作,都需要進行數組挪動操作。

小白: 那這個就費勁了

慶哥: 是啊,所以數組啊,插入刪除效率並不高,最壞的情況,時間複雜度是O(n),好的情況那就是尾部操作,那就是O(1)了,這個知道吧

小白: 嗯嗯,知道,那查找效率是不是高啊

慶哥: 這裡我覺得大家都有個誤區,什麼嘞,數組如果是使用下標訪問,那自然效率高,可是我們平常查找的話是根據數值也不都是下標啊,這樣就需要遍歷數組找到我們想要的,這個要明白

小白: 是啊,你這樣一說,我倒知道了

數組擴容

慶哥: 對了,想一下,如果數組滿了咋辦

小白: 這個啊,數組滿了,就無法插入新的元素,這樣的話就需要對數組擴容,這個擴容,我想想,數組一旦初始化完成,就是固定不變得,這樣的話,是不是需要新創建一個數組啊

慶哥: 對的,數組一旦初始化完成,在內存中的空間就已經固定了下來,即使某個元素被清空,但其所佔的空間依然是保留的,因此數組長度是不能被改變的,要想改變數組的空間就必須進行數組擴容。

小白: 那這個是不是就需要把原數組的數據全部複製過去啊

慶哥: 對的,有如下簡單代碼

來吧!給你不一樣的數組深入講解

小白: 哦哦,明白了

完!!!

ps:數組這塊怎麼說嘞,反正寫的有點痛苦,現在已經是夜裡兩點了,先到這吧,如有問題,歡迎大家留言討論!

如果大家感覺本文有幫助,請推薦本文,也歡迎大家通過評論來交流。

我是一名從事多年開發的java老程序猿員,目前辭職在做自己的java私人訂製課程,今年年初我花了一個月整理了一份最適合2019年學習的java學習乾貨資料,從最基礎的javase到spring各種框架都有整理,送給每一位java小夥伴,想要獲取的可以關注我的頭條號並在後臺私信我:02,即可免費獲取。

鏈接:https://url.cn/50BF56s


分享到:


相關文章: