2012-04-10 59 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];

这需要做简单的数学来找到合适的索引。不是直接访问数组,而是编写一个函数来完成数学计算,调用代码应该使用该数学函数。