由淺到深帶你詳談Java實現(xiàn)數(shù)組擴容的三種方式
1.新建一個數(shù)組,把原來數(shù)組的內(nèi)容搬到新數(shù)組中。
這種方法實現(xiàn)的思路是:先新建一個數(shù)組(前提條件是長度得比原來的長),然后把原來數(shù)組的內(nèi)容搬到新數(shù)組中.
案例分析:
public static void main(String[] args) { // 利用函數(shù)的方法進行數(shù)組的擴充 // 定義一個小型的數(shù)組 int[] a = { 1, 2, 3, 4, 5 }; // 調(diào)用擴容函數(shù) a= expand1(a); // 測試是否擴容完成,輸出此時數(shù)組a中的值 for (int i = 0; i < a.length; i++) { System.out.println("數(shù)組元素:" + a[i]); } } // 擴容函數(shù)1, public static int[] expand1(int a[]) { // 定義一個新數(shù)組b,并為其賦值長度為數(shù)組a的二倍 int b[] = new int[a.length * 2]; // 將數(shù)組a的元素循環(huán)遍歷到數(shù)組b中 for (int i = 0; i < a.length; i++) { b[i] = a[i]; } System.out.println("數(shù)組擴容方法1:" + Arrays.toString(b)); return b; }
輸出結(jié)果:
數(shù)組擴容方法1:[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
數(shù)組元素:1
數(shù)組元素:2
數(shù)組元素:3
數(shù)組元素:4
數(shù)組元素:5
數(shù)組元素:0
數(shù)組元素:0
數(shù)組元素:0
數(shù)組元素:0
數(shù)組元素:0
2.使用system.arraycopy()
常用作數(shù)組的擴容,如ArrayList底層數(shù)組的擴容
方法解析:
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
通過上面的源碼可以看到,arraycopy它是一個靜態(tài)本地方法,由虛擬機實現(xiàn),效率自然比用java一個個復制高。
將指定源數(shù)組中的數(shù)組從指定位置開始復制到目標數(shù)組的指定位置。 陣列組件的一個子序列被從通過引用的源陣列復制src被引用的目標陣列dest 。 復制的組件數(shù)等于length參數(shù)。 在位置的部件srcPos通過srcPos+length-1源陣列中的被復制到的位置destPos通過destPos+length-1分別,目的地陣列。
大白話總結(jié):
從源數(shù)組src取元素,范圍為下標srcPos到srcPos+length-1,取出共length個元素,存放到目標數(shù)組中,存放位置為下標destPos到destPos+length-1。簡單說,就是數(shù)組間的復制。
參數(shù)解釋:
- src 源數(shù)組;
- srcPos 源數(shù)組中的起始位置;
- dest 目標數(shù)組;
- destPos 目標數(shù)組中的起始位置;
- length 要復制的數(shù)組元素的數(shù)量
注意事項:
- 1.若src和dest參數(shù)引用相同的數(shù)組對象,則執(zhí)行復制,就好像位置 srcPos 到 srcPos + length - 1 的組件首次復制到具有 length 組件的臨時數(shù)組,然后臨時數(shù)組的內(nèi)容被復制到位置 destPos 通過目標數(shù)組的 destPos + length - 1 。
- 2.若 src 是 null ,則拋java.lang.NullPointerException異常,并且不修改目標陣列。案例演示如下:
public static void main(String[] args) { int[] src = { 1, 2, 3, 4 }; int[] dest = null; System.arraycopy(src, 0, dest, 0, 4);//拋異常:java.lang.NullPointerException for (Object o : dest) { System.out.println(o); } }
解析:
src源數(shù)組共有4個元素,索引是0-3,
dest目標數(shù)組為null,
當執(zhí)行System.arraycopy(src, 0, dest, 0, 4);時,
第一步:由于dest為null,arraycopy方法檢查到null操作,所以就會拋空指針異常。(如果你想深入理解,可以去了解一下arraycopy的底層源碼。)
- 3.srcPos 的設置主要看 destPos 和 length ,若 srcPos 該位置往后的元素少于要復制的數(shù)組元素的數(shù)量(即 srcPos + length 的長度已經(jīng)超過源數(shù)組元素的數(shù)量/個數(shù),不是長度/索引;或者 destPos + length 的長度已經(jīng)超過目標數(shù)組元素的數(shù)量/個數(shù),不是長度/索引;),則會拋java.lang.ArrayIndexOutOfBoundsException異常。案例演示如下:
//src.length < srcPos + length public static void main(String[] args) { String[] src = { "aa", "bb", "cc", "dd" }; String[] dest = new String[] { "a", "b", "c", "d", "e" }; System.arraycopy(src, 1, dest, 1, 4);//拋異常java.lang.ArrayIndexOutOfBoundsException for (Object o : dest) { System.out.println(o); } }
解析:
src源數(shù)組共有4個元素,索引是0-3,
dest目標數(shù)組共有5個元素,索引是0-4,
當執(zhí)行System.arraycopy(src, 1, dest, 1, 4);時,
第一步:srcPos為1,即從源數(shù)組(src)中,從下標1開始取,
第二步:length為4,即取4個元素,也就是src[1]-src[4],即從“bb”之后要取4個元素,這時源數(shù)組(src)只有“bb”、“cc”、“dd” 3個元素,明顯不夠4個元素,所以就會拋數(shù)組索引越界異常。
//dest.length < destPos + length public static void main(String[] args) { String[] src = { "aa", "bb", "cc", "dd" }; String[] dest = new String[] { "a", "b", "c", "d", "e" }; System.arraycopy(src, 0, dest, 2, 4);//拋異常java.lang.ArrayIndexOutOfBoundsException for (Object o : dest) { System.out.println(o); } }
解析:
src源數(shù)組共有4個元素,索引是0-3,
dest目標數(shù)組共有5個元素,索引是0-4,
當執(zhí)行System.arraycopy(src, 0, dest, 2, 4);時,
第一步:srcPos為0,即從源數(shù)組(src)中,從下標0開始取,
第二步:length為4,即取4個元素,也就是src[0]-src[3],即從“aa”之后要取4個元素;分別是“aa”、“bb”、“cc”、“dd” 4個元素
第三步:把從源數(shù)組(src)取出的數(shù)(“aa”、“bb”、“cc”、“dd” 4個元素),按順序,存放到目標數(shù)組(dest)中,從下標2開始存,存4個,也就是dest[2]-dest[5],由于dest目標數(shù)組最大索引為4,只能存放“aa”、“bb”、“cc”,存不下“dd”,即不夠容量存放4個數(shù),所以就會拋數(shù)組索引越界異常。
- 4.若 dest 是 null ,則拋java.lang.NullPointerException異常,同上/參考2。
- 5.destPos 的設置主要看 srcPos 和 length ,若 destPos 該位置容納不下要復制的數(shù)組元素(即 destPos + length 的長度已經(jīng)超過目標數(shù)組元素的數(shù)量/個數(shù),不是長度/索引;或者 srcPos + length 的長度已經(jīng)超過源數(shù)組元素的數(shù)量/個數(shù),不是長度/索引;),則會拋java.lang.ArrayIndexOutOfBoundsException異常。同上/參考3。
- 6.length 的設置主要看 srcPos 和 destPos,若 length 超出源數(shù)組中的起始位置之后的元素數(shù)量(即 srcPos 往后的元素數(shù)量/個數(shù),不是長度/索引 )或者 length 超出目標數(shù)組中的起始位置的元素數(shù)量(即 destPos 往后的元素數(shù)量/個數(shù),不是長度/索引),則會拋java.lang.ArrayIndexOutOfBoundsException異常。
// length > srcPos.length - srcPos[index] public static void main(String[] args) { String[] src = { "aa", "bb", "cc", "dd" }; String[] dest = new String[] { "a", "b", "c", "d", "e" }; System.arraycopy(src, 2, dest, 2, 3);// 拋異常java.lang.ArrayIndexOutOfBoundsException for (Object o : dest) { System.out.println(o); } }
解析:
src源數(shù)組共有4個元素,索引是0-3,
dest目標數(shù)組共有5個元素,索引是0-4,
當執(zhí)行System.arraycopy(src, 2, dest, 2, 3);時,
第一步:srcPos為2,即從源數(shù)組(src)中,從下標2開始取,
第二步:length為3,即取3個元素,也就是src[2]-src[4],即從“cc”之后要取3個元素,這時源數(shù)組(src)只有“cc”、“dd” 2個元素,明顯不夠3個元素,所以就會拋數(shù)組索引越界異常。
// length > destPos.length - destPos[index] public static void main(String[] args) { String[] src = { "aa", "bb", "cc", "dd" }; String[] dest = new String[] { "a", "b", "c", "d", "e" }; System.arraycopy(src, 2, dest, 4, 2);// 拋異常java.lang.ArrayIndexOutOfBoundsException for (Object o : dest) { System.out.println(o); } }
解析:
src源數(shù)組共有4個元素,索引是0-3,
dest目標數(shù)組共有5個元素,索引是0-4,
當執(zhí)行System.arraycopy(src, 2, dest, 4, 2);時,
第一步:srcPos為2,即從源數(shù)組(src)中,從下標2開始取,
第二步:length為2,即取2個元素,也就是src[2]-src[3],即從“cc”之后要取2個元素,分別是“cc”、“dd” 2個元素;
第三步:把從源數(shù)組(src)取出的數(shù)(“cc”、“dd” 2個元素),按順序,存放到目標數(shù)組(dest)中,從下標4開始存,存2個,也就是dest[4]-dest[5],由于dest目標數(shù)組最大索引為4,只能存放“cc”,存不下“dd”,即不夠容量存放2個數(shù),所以就會拋數(shù)組索引越界異常。
- 7.當srcPos 、destPos 和 length 任意一個參數(shù)小于0時,則會拋
java.lang.ArrayIndexOutOfBoundsException
異常。
//srcPos < 0 || destPos < 0 || length < 0 public static void main(String[] args) { String[] src = { "aa", "bb", "cc", "dd" }; String[] dest = new String[] { "a", "b", "c", "d", "e" }; System.arraycopy(src, -1, dest, 1, 1);//拋異常java.lang.ArrayIndexOutOfBoundsException for (Object o : dest) { System.out.println(o); } }
解析:
src源數(shù)組共有04個元素,索引是0-3,
dest目標數(shù)組共有5個元素,索引是0-4,
當執(zhí)行System.arraycopy(src, -1, dest, 1, 1);時,
第一步:srcPos為-1,不在索引0-3范圍內(nèi),所以就會拋數(shù)組索引越界異常。
??補充點:
- 8.當源數(shù)組和目標數(shù)組兩組數(shù)據(jù)的元素類型不匹配時,則會拋
java.lang.ArrayStoreException
異常,并且不修改目標數(shù)組。
public static void main(String[] args) { int[] src = { 1, 2, 3, 4 }; Object[] dest = new Object[3]; System.arraycopy(src, 0, dest, 0, 3);//拋異常java.lang.ArrayStoreException for (Object o : dest) { System.out.println(o); } }
或
public static void main(String[] args) { Object[] src = { 1, 2, 3, 4 }; int[] dest = new int[3]; System.arraycopy(src, 0, dest, 0, 3);// 拋異常java.lang.ArrayStoreException for (Object o : dest) { System.out.println(o); } }
若源數(shù)組src的元素為目標數(shù)組dest的元素的子類時,是可以復制的,特殊地,如int不是Object的子類(或者說,不存在繼承的概念),所以,上述代碼中,如果把int[] src = {1, 2, 3, 4};改為Integer[] src = {1, 2, 3, 4};,是不會報錯的。
public static void main(String[] args) { Integer[] src = { 1, 2, 3, 4 }; Object[] dest = new Object[3]; System.arraycopy(src, 0, dest, 0, 3); for (Object o : dest) { System.out.println(o); } }
輸出結(jié)果:
1
2
3
或
public static void main(String[] args) { Object[] src = { 1, 2, 3, 4 }; Integer[] dest = new Integer[3]; System.arraycopy(src, 0, dest, 0, 3); for (Object o : dest) { System.out.println(o); } }
輸出結(jié)果:
1
2
3
總結(jié):
拋java.lang.ArrayStoreException
異常(由于類型不匹配, src陣列中的元素無法存儲到 dest陣列中),常見的就是:
- src參數(shù)引用的對象不是數(shù)組。
- dest參數(shù)引用的對象不是數(shù)組。
- src參數(shù)和dest參數(shù)引用其組件類型為不同基元類型的數(shù)組。
- src參數(shù)引用具有基本組件類型的數(shù)組,而dest參數(shù)引用具有引用組件類型的數(shù)組。
- src參數(shù)引用具有引用組件類型的數(shù)組,而dest參數(shù)引用具有基本組件類型的數(shù)組。
拋java.lang.ArrayIndexOutOfBoundsException
異常(如果復制會導致訪問數(shù)組邊界外的數(shù)據(jù)),常見的就是:
- srcPos論點是否定的。
- destPos參數(shù)為負數(shù)。
- length參數(shù)為負數(shù)。
- srcPos+length大于src.length ,源數(shù)組的長度。
- destPos+length大于dest.length ,目標數(shù)組的長度。
拋java.lang.NullPointerException
異常,常見的就是:
- src或 dest 是 null 。
案例分析:
public static void main(String[] args) { // 利用函數(shù)的方法進行數(shù)組的擴充 // 定義一個小型的數(shù)組 int[] a = { 1, 2, 3, 4, 5 }; // 調(diào)用擴容函數(shù) a= expand2(a); // 測試是否擴容完成,輸出此時數(shù)組a中的值 for (int i = 0; i < a.length; i++) { System.out.println("數(shù)組元素:" + a[i]); } } // 數(shù)組擴容方法2,利用系統(tǒng)函數(shù)arraycopy進行擴容 public static int[] expand2(int a[]) { int[] b = new int[a.length * 2]; // 系統(tǒng)函數(shù)進行擴容,將a[]的值賦值到b[]中,共a.length個長度。 System.arraycopy(a, 0, b, 0, a.length); System.out.println("數(shù)組擴容方法2:" + Arrays.toString(b)); return b; }
輸出結(jié)果:
數(shù)組擴容方法2:[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
數(shù)組元素:1
數(shù)組元素:2
數(shù)組元素:3
數(shù)組元素:4
數(shù)組元素:5
數(shù)組元素:0
數(shù)組元素:0
數(shù)組元素:0
數(shù)組元素:0
數(shù)組元素:0
3.使用java.util.Arrays.copyOf()
方法解析:
Arrays.copyof是用于數(shù)組進行復制時常使用的方法,本身在Arrays類里面,而之所以能這么使用而不用創(chuàng)建對象在于該方法本身由static修飾,被static修飾的方法可以在該類創(chuàng)建對象前載入jvm。
Arrays.copyof有眾多的重載方法,以最典型的Arrays.copyOf(srcArray, length);為例進行分析,源碼如下:
public static long[] copyOf(long[] original, int newLength) { long[] copy = new long[newLength]; System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); return copy; }
通過上面的代碼可以看出,其本質(zhì)是調(diào)用了System.arraycopy方法。
- 1.先產(chǎn)生一個新的數(shù)組
- 2.調(diào)用arraycopy方法
- 3.返回產(chǎn)生的新數(shù)組
參數(shù)解釋:
- original 源數(shù)組
- newLength 新擴容長度
注意事項:
當我們將原來的數(shù)組進行擴容的時候,調(diào)用該方法產(chǎn)生了一個新的數(shù)組,而將擴容后的數(shù)組賦值給原來的數(shù)組的時候,原數(shù)組指向新產(chǎn)生的數(shù)組,但其原數(shù)組的內(nèi)容依然在內(nèi)存中,等待jvm回收,在這段時間中其實是造成了內(nèi)存的浪費,所以使用該方法盡管簡便,實際上有一定的不足。
案例分析:
public static void main(String[] args) { // 利用函數(shù)的方法進行數(shù)組的擴充 // 定義一個小型的數(shù)組 int[] a = { 1, 2, 3, 4, 5 }; // 調(diào)用擴容函數(shù) a= expand3(a); // 測試是否擴容完成,輸出此時數(shù)組a中的值 for (int i = 0; i < a.length; i++) { System.out.println("數(shù)組元素:" + a[i]); } } // 數(shù)組擴容方法3,利用系統(tǒng)函數(shù)copyOf進行擴容 public static int[] expand3(int a[]) { int[] b = java.util.Arrays.copyOf(a, a.length * 2); System.out.println("數(shù)組擴容方法3:" + Arrays.toString(b)); return b; }
輸出結(jié)果:
數(shù)組擴容方法3:[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
數(shù)組元素:1
數(shù)組元素:2
數(shù)組元素:3
數(shù)組元素:4
數(shù)組元素:5
數(shù)組元素:0
數(shù)組元素:0
數(shù)組元素:0
數(shù)組元素:0
數(shù)組元素:0
Java數(shù)組擴容的原理: |
- Java數(shù)組對象的大小是固定不變的,數(shù)組對象是不可擴容的。
- 利用數(shù)組復制方法可以變通的實現(xiàn)數(shù)組擴容。
- System.arraycopy()可以復制數(shù)組。
- Arrays.copyOf()可以簡便的創(chuàng)建數(shù)組副本。
- 創(chuàng)建數(shù)組副本的同時將數(shù)組長度增加就變相的實現(xiàn)了數(shù)組的擴容。
總結(jié):
只有數(shù)組為一維數(shù)組,并且元素為基本類型或String類型時,才是深拷貝,其它都屬于淺拷貝。
拷貝的兩層含義,對應了淺拷貝和深拷貝的概念,做了第一層,就是淺拷貝,做到第二層,就是深拷貝。
- 淺拷貝:將原對象或原數(shù)組的引用直接賦給新對象,新數(shù)組,新對象/數(shù)組只是原對象的一個引用。
- 深拷貝:創(chuàng)建一個新的對象和數(shù)組,將原對象的各項屬性的“值”(數(shù)組的所有元素)拷貝過來,是“值”而不是“引用”。
很容易可以想到,淺拷貝比深拷貝要更快,但是,從拷貝的意義上來看,淺拷貝相較于深拷貝,要欠缺一點,即一個是效率問題,一個是內(nèi)存占用問題。
以上就是由淺到深帶你詳談Java實現(xiàn)數(shù)組擴容的三種方式的詳細內(nèi)容,更多關(guān)于Java實現(xiàn)數(shù)組擴容的三種方式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解Java接口簽名(Signature)實現(xiàn)方案
這篇文章主要介紹了Java接口簽名(Signature)實現(xiàn)方案?,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-01-01解決MyBatisPlus的updateBatchById()批量修改失效問題
這篇文章主要介紹了解決MyBatisPlus的updateBatchById()批量修改失效問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-08-08Mybatis如何使用動態(tài)語句實現(xiàn)批量刪除(delete結(jié)合foreach)
這篇文章主要介紹了Mybatis如何使用動態(tài)語句實現(xiàn)批量刪除(delete結(jié)合foreach),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03