由淺到深帶你詳談Java實(shí)現(xiàn)數(shù)組擴(kuò)容的三種方式
1.新建一個(gè)數(shù)組,把原來數(shù)組的內(nèi)容搬到新數(shù)組中。
這種方法實(shí)現(xiàn)的思路是:先新建一個(gè)數(shù)組(前提條件是長(zhǎng)度得比原來的長(zhǎng)),然后把原來數(shù)組的內(nèi)容搬到新數(shù)組中.
案例分析:
public static void main(String[] args) {
// 利用函數(shù)的方法進(jìn)行數(shù)組的擴(kuò)充
// 定義一個(gè)小型的數(shù)組
int[] a = { 1, 2, 3, 4, 5 };
// 調(diào)用擴(kuò)容函數(shù)
a= expand1(a);
// 測(cè)試是否擴(kuò)容完成,輸出此時(shí)數(shù)組a中的值
for (int i = 0; i < a.length; i++) {
System.out.println("數(shù)組元素:" + a[i]);
}
}
// 擴(kuò)容函數(shù)1,
public static int[] expand1(int a[]) {
// 定義一個(gè)新數(shù)組b,并為其賦值長(zhǎng)度為數(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ù)組擴(kuò)容方法1:" + Arrays.toString(b));
return b;
}輸出結(jié)果:
數(shù)組擴(kuò)容方法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ù)組的擴(kuò)容,如ArrayList底層數(shù)組的擴(kuò)容
方法解析:
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
通過上面的源碼可以看到,arraycopy它是一個(gè)靜態(tài)本地方法,由虛擬機(jī)實(shí)現(xiàn),效率自然比用java一個(gè)個(gè)復(fù)制高。
將指定源數(shù)組中的數(shù)組從指定位置開始復(fù)制到目標(biāo)數(shù)組的指定位置。 陣列組件的一個(gè)子序列被從通過引用的源陣列復(fù)制src被引用的目標(biāo)陣列dest 。 復(fù)制的組件數(shù)等于length參數(shù)。 在位置的部件srcPos通過srcPos+length-1源陣列中的被復(fù)制到的位置destPos通過destPos+length-1分別,目的地陣列。
大白話總結(jié):
從源數(shù)組src取元素,范圍為下標(biāo)srcPos到srcPos+length-1,取出共length個(gè)元素,存放到目標(biāo)數(shù)組中,存放位置為下標(biāo)destPos到destPos+length-1。簡(jiǎn)單說,就是數(shù)組間的復(fù)制。
參數(shù)解釋:
- src 源數(shù)組;
- srcPos 源數(shù)組中的起始位置;
- dest 目標(biāo)數(shù)組;
- destPos 目標(biāo)數(shù)組中的起始位置;
- length 要復(fù)制的數(shù)組元素的數(shù)量
注意事項(xiàng):
- 1.若src和dest參數(shù)引用相同的數(shù)組對(duì)象,則執(zhí)行復(fù)制,就好像位置 srcPos 到 srcPos + length - 1 的組件首次復(fù)制到具有 length 組件的臨時(shí)數(shù)組,然后臨時(shí)數(shù)組的內(nèi)容被復(fù)制到位置 destPos 通過目標(biāo)數(shù)組的 destPos + length - 1 。
- 2.若 src 是 null ,則拋java.lang.NullPointerException異常,并且不修改目標(biāo)陣列。案例演示如下:
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個(gè)元素,索引是0-3,
dest目標(biāo)數(shù)組為null,
當(dāng)執(zhí)行System.arraycopy(src, 0, dest, 0, 4);時(shí),
第一步:由于dest為null,arraycopy方法檢查到null操作,所以就會(huì)拋空指針異常。(如果你想深入理解,可以去了解一下arraycopy的底層源碼。)
- 3.srcPos 的設(shè)置主要看 destPos 和 length ,若 srcPos 該位置往后的元素少于要復(fù)制的數(shù)組元素的數(shù)量(即 srcPos + length 的長(zhǎng)度已經(jīng)超過源數(shù)組元素的數(shù)量/個(gè)數(shù),不是長(zhǎng)度/索引;或者 destPos + length 的長(zhǎng)度已經(jīng)超過目標(biāo)數(shù)組元素的數(shù)量/個(gè)數(shù),不是長(zhǎng)度/索引;),則會(huì)拋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個(gè)元素,索引是0-3,
dest目標(biāo)數(shù)組共有5個(gè)元素,索引是0-4,
當(dāng)執(zhí)行System.arraycopy(src, 1, dest, 1, 4);時(shí),
第一步:srcPos為1,即從源數(shù)組(src)中,從下標(biāo)1開始取,
第二步:length為4,即取4個(gè)元素,也就是src[1]-src[4],即從“bb”之后要取4個(gè)元素,這時(shí)源數(shù)組(src)只有“bb”、“cc”、“dd” 3個(gè)元素,明顯不夠4個(gè)元素,所以就會(huì)拋數(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個(gè)元素,索引是0-3,
dest目標(biāo)數(shù)組共有5個(gè)元素,索引是0-4,
當(dāng)執(zhí)行System.arraycopy(src, 0, dest, 2, 4);時(shí),
第一步:srcPos為0,即從源數(shù)組(src)中,從下標(biāo)0開始取,
第二步:length為4,即取4個(gè)元素,也就是src[0]-src[3],即從“aa”之后要取4個(gè)元素;分別是“aa”、“bb”、“cc”、“dd” 4個(gè)元素
第三步:把從源數(shù)組(src)取出的數(shù)(“aa”、“bb”、“cc”、“dd” 4個(gè)元素),按順序,存放到目標(biāo)數(shù)組(dest)中,從下標(biāo)2開始存,存4個(gè),也就是dest[2]-dest[5],由于dest目標(biāo)數(shù)組最大索引為4,只能存放“aa”、“bb”、“cc”,存不下“dd”,即不夠容量存放4個(gè)數(shù),所以就會(huì)拋數(shù)組索引越界異常。
- 4.若 dest 是 null ,則拋java.lang.NullPointerException異常,同上/參考2。
- 5.destPos 的設(shè)置主要看 srcPos 和 length ,若 destPos 該位置容納不下要復(fù)制的數(shù)組元素(即 destPos + length 的長(zhǎng)度已經(jīng)超過目標(biāo)數(shù)組元素的數(shù)量/個(gè)數(shù),不是長(zhǎng)度/索引;或者 srcPos + length 的長(zhǎng)度已經(jīng)超過源數(shù)組元素的數(shù)量/個(gè)數(shù),不是長(zhǎng)度/索引;),則會(huì)拋java.lang.ArrayIndexOutOfBoundsException異常。同上/參考3。
- 6.length 的設(shè)置主要看 srcPos 和 destPos,若 length 超出源數(shù)組中的起始位置之后的元素?cái)?shù)量(即 srcPos 往后的元素?cái)?shù)量/個(gè)數(shù),不是長(zhǎng)度/索引 )或者 length 超出目標(biāo)數(shù)組中的起始位置的元素?cái)?shù)量(即 destPos 往后的元素?cái)?shù)量/個(gè)數(shù),不是長(zhǎng)度/索引),則會(huì)拋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個(gè)元素,索引是0-3,
dest目標(biāo)數(shù)組共有5個(gè)元素,索引是0-4,
當(dāng)執(zhí)行System.arraycopy(src, 2, dest, 2, 3);時(shí),
第一步:srcPos為2,即從源數(shù)組(src)中,從下標(biāo)2開始取,
第二步:length為3,即取3個(gè)元素,也就是src[2]-src[4],即從“cc”之后要取3個(gè)元素,這時(shí)源數(shù)組(src)只有“cc”、“dd” 2個(gè)元素,明顯不夠3個(gè)元素,所以就會(huì)拋數(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個(gè)元素,索引是0-3,
dest目標(biāo)數(shù)組共有5個(gè)元素,索引是0-4,
當(dāng)執(zhí)行System.arraycopy(src, 2, dest, 4, 2);時(shí),
第一步:srcPos為2,即從源數(shù)組(src)中,從下標(biāo)2開始取,
第二步:length為2,即取2個(gè)元素,也就是src[2]-src[3],即從“cc”之后要取2個(gè)元素,分別是“cc”、“dd” 2個(gè)元素;
第三步:把從源數(shù)組(src)取出的數(shù)(“cc”、“dd” 2個(gè)元素),按順序,存放到目標(biāo)數(shù)組(dest)中,從下標(biāo)4開始存,存2個(gè),也就是dest[4]-dest[5],由于dest目標(biāo)數(shù)組最大索引為4,只能存放“cc”,存不下“dd”,即不夠容量存放2個(gè)數(shù),所以就會(huì)拋數(shù)組索引越界異常。
- 7.當(dāng)srcPos 、destPos 和 length 任意一個(gè)參數(shù)小于0時(shí),則會(huì)拋
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個(gè)元素,索引是0-3,
dest目標(biāo)數(shù)組共有5個(gè)元素,索引是0-4,
當(dāng)執(zhí)行System.arraycopy(src, -1, dest, 1, 1);時(shí),
第一步:srcPos為-1,不在索引0-3范圍內(nèi),所以就會(huì)拋數(shù)組索引越界異常。
??補(bǔ)充點(diǎn):
- 8.當(dāng)源數(shù)組和目標(biāo)數(shù)組兩組數(shù)據(jù)的元素類型不匹配時(shí),則會(huì)拋
java.lang.ArrayStoreException異常,并且不修改目標(biāo)數(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的元素為目標(biāo)數(shù)組dest的元素的子類時(shí),是可以復(fù)制的,特殊地,如int不是Object的子類(或者說,不存在繼承的概念),所以,上述代碼中,如果把int[] src = {1, 2, 3, 4};改為Integer[] src = {1, 2, 3, 4};,是不會(huì)報(bào)錯(cuò)的。
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陣列中的元素?zé)o法存儲(chǔ)到 dest陣列中),常見的就是:
- src參數(shù)引用的對(duì)象不是數(shù)組。
- dest參數(shù)引用的對(duì)象不是數(shù)組。
- src參數(shù)和dest參數(shù)引用其組件類型為不同基元類型的數(shù)組。
- src參數(shù)引用具有基本組件類型的數(shù)組,而dest參數(shù)引用具有引用組件類型的數(shù)組。
- src參數(shù)引用具有引用組件類型的數(shù)組,而dest參數(shù)引用具有基本組件類型的數(shù)組。
拋java.lang.ArrayIndexOutOfBoundsException異常(如果復(fù)制會(huì)導(dǎo)致訪問數(shù)組邊界外的數(shù)據(jù)),常見的就是:
- srcPos論點(diǎn)是否定的。
- destPos參數(shù)為負(fù)數(shù)。
- length參數(shù)為負(fù)數(shù)。
- srcPos+length大于src.length ,源數(shù)組的長(zhǎng)度。
- destPos+length大于dest.length ,目標(biāo)數(shù)組的長(zhǎng)度。
拋java.lang.NullPointerException異常,常見的就是:
- src或 dest 是 null 。
案例分析:
public static void main(String[] args) {
// 利用函數(shù)的方法進(jìn)行數(shù)組的擴(kuò)充
// 定義一個(gè)小型的數(shù)組
int[] a = { 1, 2, 3, 4, 5 };
// 調(diào)用擴(kuò)容函數(shù)
a= expand2(a);
// 測(cè)試是否擴(kuò)容完成,輸出此時(shí)數(shù)組a中的值
for (int i = 0; i < a.length; i++) {
System.out.println("數(shù)組元素:" + a[i]);
}
}
// 數(shù)組擴(kuò)容方法2,利用系統(tǒng)函數(shù)arraycopy進(jìn)行擴(kuò)容
public static int[] expand2(int a[]) {
int[] b = new int[a.length * 2];
// 系統(tǒng)函數(shù)進(jìn)行擴(kuò)容,將a[]的值賦值到b[]中,共a.length個(gè)長(zhǎng)度。
System.arraycopy(a, 0, b, 0, a.length);
System.out.println("數(shù)組擴(kuò)容方法2:" + Arrays.toString(b));
return b;
}輸出結(jié)果:
數(shù)組擴(kuò)容方法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ù)組進(jìn)行復(fù)制時(shí)常使用的方法,本身在Arrays類里面,而之所以能這么使用而不用創(chuàng)建對(duì)象在于該方法本身由static修飾,被static修飾的方法可以在該類創(chuàng)建對(duì)象前載入jvm。
Arrays.copyof有眾多的重載方法,以最典型的Arrays.copyOf(srcArray, length);為例進(jìn)行分析,源碼如下:
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)生一個(gè)新的數(shù)組
- 2.調(diào)用arraycopy方法
- 3.返回產(chǎn)生的新數(shù)組
參數(shù)解釋:
- original 源數(shù)組
- newLength 新擴(kuò)容長(zhǎng)度
注意事項(xiàng):
當(dāng)我們將原來的數(shù)組進(jìn)行擴(kuò)容的時(shí)候,調(diào)用該方法產(chǎn)生了一個(gè)新的數(shù)組,而將擴(kuò)容后的數(shù)組賦值給原來的數(shù)組的時(shí)候,原數(shù)組指向新產(chǎn)生的數(shù)組,但其原數(shù)組的內(nèi)容依然在內(nèi)存中,等待jvm回收,在這段時(shí)間中其實(shí)是造成了內(nèi)存的浪費(fèi),所以使用該方法盡管簡(jiǎn)便,實(shí)際上有一定的不足。
案例分析:
public static void main(String[] args) {
// 利用函數(shù)的方法進(jìn)行數(shù)組的擴(kuò)充
// 定義一個(gè)小型的數(shù)組
int[] a = { 1, 2, 3, 4, 5 };
// 調(diào)用擴(kuò)容函數(shù)
a= expand3(a);
// 測(cè)試是否擴(kuò)容完成,輸出此時(shí)數(shù)組a中的值
for (int i = 0; i < a.length; i++) {
System.out.println("數(shù)組元素:" + a[i]);
}
}
// 數(shù)組擴(kuò)容方法3,利用系統(tǒng)函數(shù)copyOf進(jìn)行擴(kuò)容
public static int[] expand3(int a[]) {
int[] b = java.util.Arrays.copyOf(a, a.length * 2);
System.out.println("數(shù)組擴(kuò)容方法3:" + Arrays.toString(b));
return b;
}輸出結(jié)果:
數(shù)組擴(kuò)容方法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ù)組擴(kuò)容的原理: |
- Java數(shù)組對(duì)象的大小是固定不變的,數(shù)組對(duì)象是不可擴(kuò)容的。
- 利用數(shù)組復(fù)制方法可以變通的實(shí)現(xiàn)數(shù)組擴(kuò)容。
- System.arraycopy()可以復(fù)制數(shù)組。
- Arrays.copyOf()可以簡(jiǎn)便的創(chuàng)建數(shù)組副本。
- 創(chuàng)建數(shù)組副本的同時(shí)將數(shù)組長(zhǎng)度增加就變相的實(shí)現(xiàn)了數(shù)組的擴(kuò)容。
總結(jié):
只有數(shù)組為一維數(shù)組,并且元素為基本類型或String類型時(shí),才是深拷貝,其它都屬于淺拷貝。
拷貝的兩層含義,對(duì)應(yīng)了淺拷貝和深拷貝的概念,做了第一層,就是淺拷貝,做到第二層,就是深拷貝。
- 淺拷貝:將原對(duì)象或原數(shù)組的引用直接賦給新對(duì)象,新數(shù)組,新對(duì)象/數(shù)組只是原對(duì)象的一個(gè)引用。
- 深拷貝:創(chuàng)建一個(gè)新的對(duì)象和數(shù)組,將原對(duì)象的各項(xiàng)屬性的“值”(數(shù)組的所有元素)拷貝過來,是“值”而不是“引用”。
很容易可以想到,淺拷貝比深拷貝要更快,但是,從拷貝的意義上來看,淺拷貝相較于深拷貝,要欠缺一點(diǎn),即一個(gè)是效率問題,一個(gè)是內(nèi)存占用問題。
以上就是由淺到深帶你詳談Java實(shí)現(xiàn)數(shù)組擴(kuò)容的三種方式的詳細(xì)內(nèi)容,更多關(guān)于Java實(shí)現(xiàn)數(shù)組擴(kuò)容的三種方式的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SVN導(dǎo)入maven項(xiàng)目報(bào)錯(cuò)解決方案
這篇文章主要介紹了SVN導(dǎo)入maven項(xiàng)目報(bào)錯(cuò)解決方案,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12
Spring Boot修改內(nèi)置Tomcat默認(rèn)端口號(hào)的示例
本篇文章主要介紹了Spring Boot修改內(nèi)置Tomcat端口號(hào)的示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
springboot整合JavaCV實(shí)現(xiàn)視頻截取第N幀并保存圖片
這篇文章主要為大家詳細(xì)介紹了springboot如何整合JavaCV實(shí)現(xiàn)視頻截取第N幀并保存為圖片,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-08-08
Java11?中基于嵌套關(guān)系的訪問控制優(yōu)化問題
在?Java?語言中,類和接口可以相互嵌套,這種組合之間可以不受限制的彼此訪問,包括訪問彼此的構(gòu)造函數(shù)、字段、方法,接下來通過本文給大家介紹Java11中基于嵌套關(guān)系的訪問控制優(yōu)化問題,感興趣的朋友一起看看吧2022-01-01
淺談MyBatis Example模式SQL注入風(fēng)險(xiǎn)
在使用MyBatis逆向工程生成的Example查詢模式時(shí),很多開發(fā)者看到XML中存在${}占位符就會(huì)擔(dān)心SQL注入問題,本文就來介紹一下MyBatis Example模式SQL注入風(fēng)險(xiǎn),感興趣的可以了解一下2025-10-10
SpringMVC中的SimpleUrlHandlerMapping用法詳解
這篇文章主要介紹了SpringMVC中的SimpleUrlHandlerMapping用法詳解,SimpleUrlHandlerMapping是Spring MVC中適用性最強(qiáng)的Handler Mapping類,允許明確指定URL模式和Handler的映射關(guān)系,有兩種方式聲明SimpleUrlHandlerMapping,需要的朋友可以參考下2023-10-10

