2012-04-10 90 views
9

的初始化我分配一個大陣列雙打作爲如何避免大陣

double[] x = new double[ n ]; 

其中n是大的,我想避免的初始化,以節省時間。可能嗎?

+0

將Java標記添加到您的問題 – 2016-01-01 18:19:41

+1

設置vm參數Xms4000m有幫助。它現在在一分鐘內初始化。 – 2016-01-01 18:27:50

+0

我無法編輯它,這不是我的問題。 – 2016-01-01 18:28:20

回答

10

簡短回答:不。數組創建時總是被清零。

如果您的配置文件顯示這是一個主要瓶頸,您可以考慮保留一個數組實例池,每個配置文件的長度要大於n。問題是你可能需要一個包裝對象來包含數據數組和實際使用的長度,因爲你不能再使用data.length

10

你可以使用ArrayList什麼的,並建立你的數組,因爲你需要添加元素給它?這將節省初始化時間,如果這是你的問題。

ArrayList<double> x = new ArrayList<double>(); 
+0

我認爲這是最好的選擇。所有其他答案都需要很多解決方法,這些解決方案也可能會在未來創建更多時間問題。然而,我想知道是否有Java庫創建數組,您必須初始化每個值而不是使用0自動填充? – patrickhuie19 2016-01-03 14:01:05

+6

@Nick Rolando你不能爲原始類型創建ArrayList,比如'double'。相反,您必須使用'ArrayList '。它將使用裝箱/拆箱從原始類型轉換爲引用類型。消費所有上述說法,這個答案不是最好的作者問題。 – 2016-01-06 07:29:28

+1

這是個壞主意**。通過向其添加元素來增加列表的花費明顯更高,然後將其分配(初始化)爲最初具有合適的大小。另外,如上所述,您不能擁有基元的'ArrayList',所以當元素數量很大時,這種方法也有可能產生大量的內存開銷。 – Dima 2016-01-07 17:23:31

1

只要聲明「new double [n]」語句,數組就會初始化。沒有辦法繞過它。

如果你這樣做是爲了優化的緣故,那麼我會利用你對過早優化進行讀取。如果你的程序沒有打牆,那麼它不值得優化。而且它不是你應該優化的數組。

0

您可以使用ArrayList,以節省初始化時間,然後將其轉換爲一個數組,如果你確實需要雙陣列上的工作是這樣的:

List<Double> withoutInitializing = new ArrayList<Double>(); 
Double[] nowYouConvert = (Double[]) withoutInitializing.toArray(); 

從Java文檔:

指定者:以正確的順序(從第一個元素到最後一個元素)返回包含此列表中所有元素的數組。

返回的數組將是「安全」的,因爲沒有引用它是由此列表維護的 。 (換句話說,即使列表由數組支持,該方法也必須分配 新數組)。因此調用者可以免費修改返回的數組。

此方法充當基於陣列和基於集合的 API之間的橋樑。

指定者:指定者()在集合

+1

與此相關的問題是,ArrayList訪問速度稍慢,這對我的高性能算法產生巨大影響。 – 2016-01-02 21:08:30

+0

''ArrayList'實際上是由一個數組(對象)支持的,最初由默認的構造函數賦予一些任意常量('DEFAULT_CAPACITY = 10'),然後在每次空間不足時動態地將數組重新分配給更大的數組。所以,如果你真的想要存儲n個條目,你最終會創建O(log(n))個數組,最後一個至少是添加到元素的數量的大小(可能更大)名單。您可以通過調用專門的構造函數或調用'ensureCapacity()'來確保所需的容量,從而避免重新分配。 – 2016-01-07 01:02:06

+0

所以這絕不會避免創建**並初始化**一個大數組。更不用說,你會讓Double對象在數組中包含double值而不是直接double值,考慮到wrapper對象的開銷,在內存使用方面更糟糕。 – 2016-01-07 01:03:03

0

一些解決方法如何不初始化數組。

創建一個數組,該數組保證大於最大可能數量的條目並部分填充它。

例如,您可以決定用戶永遠不會提供超過100個輸入值。然後分配尺寸100的陣列:

final int VALUES_LENGTH = 100; 
double[] values = new double[VALUES_LENGTH]; 

然後保持伴侶變量,告訴如何在陣列中許多的元素被實際使用。

int valuesSize = 0; 

現在values.length是數組值的能力,和valuesSize是陣列的當前大小。不斷添加元素到數組中,每次增加valuesSize變量。

values[valuesSize] = x; 
valuesSize++; 

這樣,valuesSize始終包含正確的元素數。 以下代碼段顯示如何將數字讀入部分填充的 數組。

int valuesSize = 0; 
Scanner in = new Scanner(System.in); 
while (in.hasNextDouble()) { 
    if (valuesSize < values.length) { 
    values[valuesSize] = in.nextDouble(); 
    valuesSize++; 
    } 
} 

在該循環結束時,valuesSize包含數組中元素的實際數量。

例如,這裏是你如何讀取任意長的序列號爲 數組,而不會耗盡空間:

int valuesSize = 0; 
while (in.hasNextDouble()) { 
    if (valuesSize == values.length) { 
     values = Arrays.copyOf(values, 2 * values.length); 
    } 
    values[valuesSize] = in.nextDouble(); 
    valuesSize++; 
} 
2

像其他人已經提到的,簡單的答案是:不,你無法避免初始化部分。除非您正在使用某些native分配或使用IntBuffer創建作爲byte buffer的視圖,否則在直接使用字節緩衝區時將是直接的。

如果您沒有使用它們,那麼爲了儘可能快地分配和初始化陣列,您需要最大限度地減少GC調用,並且必須確保JVM具有存儲和使用所需的內存該數組。

在Albert Hendriks的案例中:static int[][] lookup = new int[113088217][2],沒有至少2.3G(12 + 113088217 *(12 + 2 * 4)字節)的內存,JVM將無法分配所需的空間。請注意,我沒有添加所需的填充空間(內存對齊)。

回答爲什麼lookup = new int[2*113088217];表現更快。這是因爲處理的內存少得多,因爲我們沒有子數組(頭+元素+每個子數組的對齊),所以只需要(2 * 113088217 * 4 + 12)字節=〜804M。

5

如果您不想初始化太長的數組,您可以確定數組大小的限制,這不會讓您等待太久。我建議將一個長陣列分成更小的陣列。 定義一個保存陣列的列表。如果您的數組已填充,請將其添加到您的列表中。然後繼續填寫一個新的。

import java.util.ArrayList; 
import java.util.List; 

public class Tester { 
    private static final int LIMIT = 30; 
    private static int index = 0; 

    private static int[][] lookup; 
    private static List<int[][]> list = new ArrayList<int[][]>(); 

    public static void main(String[] args) { 
     lookup = new int[LIMIT][1]; 

     for (int i = 0; i <= 93; i++) { 
      addToArr(i); 
     } 

     list.add(lookup); 

     for (int[][] intArr : list) { 
      for (int i = 0; i < intArr.length; i++) { 
       System.out.print(intArr[i][0] + ","); 
      } 
     } 
    } 

    public static void addToArr(int value) { 
     if (index == LIMIT) { 
      list.add(lookup); 
      lookup = new int[LIMIT][1]; 
      index = 0; 
     } 
     lookup [index++][0] = value; 
    } 
} 

打印:

0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17 ,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42 ,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67 ,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92 ,93,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ,0,0,

2

** 警告 ** 不安全替代品 **

這不是一個確切的解決方案,但它可能是一個可行的選擇。這種方法有一些風險。但是如果真的有必要的話,你可以走這條路。此方法使用未記錄的sun.misc.Unsafe類來分配堆外內存以存儲雙精度值。堆外意味着它不是垃圾收集,所以你需要注意釋放相關的內存。

以下代碼基於此blog post about sun.misc.Unsafe

import java.lang.reflect.Field; 

import sun.misc.Unsafe; 

@SuppressWarnings("restriction") 
public class SuperDoubleArray { 

    private final static Unsafe unsafe = getUnsafe(); 
    private final static int INDEX_SCALE = 8; 

    private long size; 
    private long address; 

    public SuperDoubleArray(long size) { 
     this.size = size; 
     address = unsafe.allocateMemory(size * INDEX_SCALE); 
    } 

    private static Unsafe getUnsafe() { 
     try { 
      Field singleoneInstanceField = Unsafe.class.getDeclaredField("theUnsafe"); 
      singleoneInstanceField.setAccessible(true); 
      return (Unsafe) singleoneInstanceField.get(null); 
     } catch (IllegalArgumentException | SecurityException 
       | NoSuchFieldException | IllegalAccessException e) { 
      throw new RuntimeException(e); 
     } 
    } 

    public void set(long i, double value) { 
     unsafe.putDouble(address + i * INDEX_SCALE, value); 
    } 

    public double get(long idx) { 
     return unsafe.getDouble(address + idx * INDEX_SCALE); 
    } 

    public long size() { 
     return size; 
    } 

    public void deallocate() { 
     unsafe.freeMemory(address); 
    } 

} 

以下代碼將打印來自單位化內存的一些隨機double值。

SuperDoubleArray sda = new SuperDoubleArray(100); 
for (int i=0; i<sda.size(); i++) { 
    System.out.println(sda.get(i)); 
} 
sda.deallocate(); 

有沒有安全/範圍的檢查,什麼都沒有,你可以很容易崩潰的JVM有了它,可能無法與非Sun的JRE工作,甚至可能會停止在未來SUN JRE版本的工作,但它可能在某些情況下是唯一的解決方案。它也可以分配>Integer.MAX_VALUE大小的僞陣列,與Java數組不同。

java.nio.ByteBuffer.allocateDirect(...)實際上在後臺使用相同的不安全類來分配字節的緩衝區,你可以使用ByteBuffer.allocateDirect(8*size).asDoubleBuffer()它適應DoubleBuffer,但ByteBuffer.allocateDirect(...)仍在初始化用零緩衝,所以它可能有一個性能開銷。

0

據我瞭解的問題,真正的問題是與分配一個巨大的維數組,作爲在評論中提到

「靜態INT [] []查找=新INT [113088217] [2] ;不工作,雖然私人最終靜態int [] []查找=新詮釋[11308821] [2];(10倍以內)進入不到一秒鐘「

假設這是正確的,是的,數字它是骨頭緩慢。你是而不是分配一個113088217 * 2 整數單塊!您正在分配113088217個人陣列,它們是對象,它們中的每一個都必須分配在堆上:這意味着超過1億次JVM正在尋找空間,將其標記爲已用,可能在內存獲取時運行GC緊,等等......這些陣列每個都需要大量(在這些巨大的數字中)額外的內存,再加上每個都包含2個整數。

對於這個龐大的情況下:

1)切換指標,去

static int[][] lookup = new int[2][113088217]

不是分配1.13億陣列,該分配。在數組中查找時,切換兩個索引。

2)使一維數組,做自己查找

static int[] lookup = new int[2*113088217];

這需要做簡單的數學來找到合適的索引。不是直接訪問數組,而是編寫一個函數來完成數學計算,調用代碼應該使用該數學函數。