Java數組擴容的三大小結
方案1:新建數組
這種方法新建的數組必須要比原先的長度要長,然后將原來的數組內容移到新的數組中
<!--more--> int[] a = {1, 2, 3, 4, 5}; ? // 創(chuàng)建新數組,長度為源數組的兩倍 int[] b = new int[a.length * 2]; ? // 將舊數組內容復制到新數組 for (int i = 0; i < a.length; i++) { ? ? b[i] = a[i]; } ? // 新數組內容賦值給源數組 a = b; ? // 打印結果 System.out.println(Arrays.toString(a));
輸出結果
[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
方案2:Arrays.copyOf
int[] a = {1, 2, 3, 4, 5}; ? // 第一個參數是拷貝的數組,第二個參數是擴容長度,且返回一個新數組 a = Arrays.copyOf(a, a.length * 2); ? // 打印結果 System.out.println(Arrays.toString(a));
輸出結果
[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
Arrays.copyof是用于數組進行復制時常使用的方法,本身在Arrays類里面,而之所以能這么使用而不用創(chuàng)建對象在于該方法本身由static修飾,被static修飾的方法可以在該類創(chuàng)建對象前載入jvm。
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; }
通過上面的代碼可以看出,其本質是調用了System.arraycopy方法。
先產生一個新的數組然后調用arraycopy方法最后在返回產生的新數組。但是我們進行數組擴容的時候禪城了新數組,但是原數組依然存在,造成了內存的浪費。
方案3:System.arraycopy
int[] a = {1, 2, 3, 4, 5}; ? // 定義新數組,長度為源數組的兩倍 int[] b = new int[a.length * 2]; ? /** * src 需要拷貝的源數組 * srcPos 源數組中的起始位置 * dest 目標數組 * destPos 目標數組中的起始位置 * length 要復制的數組元素數量 */ System.arraycopy(a, 0, b, 0, a.length); ? // 新數組內容賦值給原數組 a = b; ? // 打印結果 System.out.println(Arrays.toString(a));
輸出結果
[1, 2, 3, 4, 5, 0, 0, 0, 0, 0]
arraycopy源碼
/** * @param src the source array. 源數組 * @param srcPos starting position in the source array. 要復制的源數組的起始位置 * @param dest the destination array. 目標數組 * @param destPos starting position in the destination data. 目標數組的起始位置 * @param length the number of array elements to be copied. 要復制的長度 * @throws IndexOutOfBoundsException if copying would cause * access of data outside array bounds. * 如果復制會導致數據的訪問超出數組邊界。 * 則會報IndexOutOfBoundsException索引越界異常 * @throws ArrayStoreException if an element in the <code>src</code> array * could not be stored into the <code>dest</code> array * because of a type mismatch. * 如果由于類型不匹配而無法將src數組中的元素存儲到dest數組中。 * 則會報 ArrayStoreException數組存儲異常 * @throws NullPointerException if either <code>src</code> or * <code>dest</code> is <code>null</code>. * 如果src或dest為null。 * 則會報NullPointerException空指針異常 */ ? public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
System.arraycopy()的幾種常用方法(普通for循環(huán)和arraycopy方法對比)
1. 從舊數組拷貝到新數組
//從舊數組拷貝到新數組 for (int i=0;i<size;i++){ arrayNew[i]=array[i]; } System.arraycopy(array, 0, arrayNew, 0, array.length);
2. 從左向右循環(huán),逐個元素向左挪一位。
//從左向右循環(huán),逐個元素向左挪一位。 for (int i = index; i < size - 1; i++) { array[i] = array[i + 1]; } System.arraycopy(array, index + 1, array, index, size - 1 - index);
3. 從右向左循環(huán),逐個元素向右挪一位。
//從右向左循環(huán),逐個元素向右挪一位。 for (int i = size - 1; i >= index; i--) { array[i + 1] = array[i]; } System.arraycopy(array, index, array, index + 1, size - index);
System.arraycopy()深層理解
深復制還是淺復制
先說結論 :
- 當數組為一維數組,且元素為基本類型或String類型時,屬于深拷貝,即原數組與新數組的元素不會相互影響。
- 當數組為多維數組,或一維數組中的元素為引用類型時,屬于淺拷貝,原數組與新數組的元素引用指向同一個對象。
引用對象
構建一個User類型源數組,復制后至target數組,比較第一個元素的內存地址,判斷結果是相同的,證明為淺復制;后修改target數組數組的隨機元素,發(fā)現原來的值也變了。
public class SystemArrayCopyTestCase { ? public static void main(String[] args) { User[] users = new User[] { new User(1), new User(2), new User(3) };// 初始化對象數組 User[] target = new User[users.length];// 新建一個目標對象數組 System.arraycopy(users, 0, target, 0, users.length);// 實現復制 System.out.println("源對象與目標對象的物理地址是否一樣:" + (users[0] == target[0] ? "淺復制" : "深復制")); //淺復制 target[0].setId(5); System.out.println("修改目標對象的屬性值后源對象users:"); for (User user : users) { System.out.println(user); } } } ? class User { } ?
System.arraycopy() 在拷貝數組的時候,采用的使用潛復制,復制結果是一維的引用變量傳遞給新數組的一維數組,修改新數組時,會影響原來的數組。
一維數組和多維數組
將一維數組作源數組,進行拷貝,產生target數組;然后修改target數組中的元素,新數組沒變,證明是值拷貝,修改新數組不會影響原來的值。
將多維數組作源數組,進行拷貝至目標數組,修改至目標數組的元素,新數組也變了,說明是二者是相同的引用,而這時改變其中任何一個數組的元素的值,其實都修改了共同數組元素的值,所以原數組和新數組的元素值都一樣了
線程是否安全(摘自網絡)
System.ayyaycopy是不安全的。
public class ArrayCopyThreadSafe { private static int[] arrayOriginal = new int[1024 * 1024 * 10]; private static int[] arraySrc = new int[1024 * 1024 * 10]; private static int[] arrayDist = new int[1024 * 1024 * 10]; private static ReentrantLock lock = new ReentrantLock(); ? private static void modify() { for (int i = 0; i < arraySrc.length; i++) { arraySrc[i] = i + 1; } } ? private static void copy() { System.arraycopy(arraySrc, 0, arrayDist, 0, arraySrc.length); } ? private static void init() { for (int i = 0; i < arraySrc.length; i++) { arrayOriginal[i] = i; arraySrc[i] = i; arrayDist[i] = 0; } } ? private static void doThreadSafeCheck() throws Exception { for (int i = 0; i < 100; i++) { System.out.println("run count: " + (i + 1)); init(); Condition condition = lock.newCondition(); ? new Thread(new Runnable() { @Override public void run() { lock.lock(); condition.signalAll(); lock.unlock(); copy(); } }).start(); ? ? lock.lock(); // 這里使用 Condition 來保證拷貝線程先已經運行了. condition.await(); lock.unlock(); ? Thread.sleep(2); // 休眠2毫秒, 確??截惒僮饕呀泩?zhí)行了, 才執(zhí)行修改操作. modify(); ? if (!Arrays.equals(arrayOriginal, arrayDist)) { throw new RuntimeException("System.arraycopy is not thread safe"); } } } ? public static void main(String[] args) throws Exception { doThreadSafeCheck(); } } ?
這個例子的具體操作是:
arrayOriginal 和 arraySrc 初始化時是相同的, 而 arrayDist 是全為零的.
啟動一個線程運行 copy() 方法來拷貝 arraySrc 到 arrayDist 中.
在主線程執(zhí)行 modify() 操作, 修改 arraySrc 的內容. 為了確保 copy() 操作先于 modify() 操作, 我使用 Condition, 并且延時了兩毫秒, 以此來保證執(zhí)行拷貝操作(即System.arraycopy) 先于修改操作.
根據第三點, 如果 System.arraycopy 是線程安全的, 那么先執(zhí)行拷貝操作, 再執(zhí)行修改操作時, 不會影響復制結果, 因此 arrayOriginal 必然等于 arrayDist; 而如果 System.arraycopy 是線程不安全的, 那么 arrayOriginal 不等于 arrayDist.
run count: 1
Exception in thread "main" java.lang.RuntimeException: System.arraycopy is not thread safe
at ArrayCopyThreadSafe.doThreadSafeCheck(ArrayCopyThreadSafe.java:54)
at ArrayCopyThreadSafe.main(ArrayCopyThreadSafe.java:60)
結論 :System.ayyaycopy是不安全的。
System.arraycopy()和for()相比誰更高效
當測試數組的范圍比較小的時候,兩者相差的時間無幾,當測試數組的長度達到百萬級別,System.arraycopy的速度優(yōu)勢就開始體現了,根據對底層的理解,System.arraycopy是對內存直接進行復制,減少了for循環(huán)過程中的尋址時間,從而提高了效能。
到此這篇關于Java數組擴容的三大小結的文章就介紹到這了,更多相關Java數組擴容內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
關于maven install報錯原因揭秘:parent.relativePath指向錯誤的本地POM文件
在使用Maven進行項目構建時,如果遇到'parent.relativePath'指向錯誤的本地POM文件的問題,可能會導致構建失敗,這通常是由于父項目POM文件的相對路徑設置錯誤、本地POM文件與父項目POM文件版本或內容不一致所致,解決方法包括檢查并修正父項目POM文件中的相對路徑設置2024-09-09HttpMessageConverter報文信息轉換器的深入講解
在Spring中內置了大量的HttpMessageConverter,通過請求頭信息中的MIME類型,選擇相應的HttpMessageConverter,這篇文章主要給大家介紹了關于HttpMessageConverter報文信息轉換器的相關資料,需要的朋友可以參考下2022-01-01MyBatis直接執(zhí)行SQL的工具SqlMapper
今天小編就為大家分享一篇關于MyBatis直接執(zhí)行SQL的工具SqlMapper,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2018-12-12Spring注解驅動之關于@Bean注解指定初始化和銷毀的方法
這篇文章主要介紹了Spring注解驅動之關于@Bean注解指定初始化和銷毀的方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09