如何避免大型数组的初始化

时间:2012-04-10 22:23:16

标签: java arrays

我分配了大量的双打作为

double[] x = new double[ n ];

其中n很大,我想避免初始化以节省时间。有可能吗?

9 个答案:

答案 0 :(得分:10)

简短回答:不会。数组在创建时总是会被清零。

如果您的分析显示这是一个主要的瓶颈,您可以考虑保留一个数组实例池,每个集的长度将大于n。问题是你可能需要一个包装器对象来包含数据数组和使用的实际长度,因为你不能再使用data.length

答案 1 :(得分:10)

您可以使用ArrayList或其他内容,并在需要添加元素时构建数组吗?如果那是你的问题,这将节省初始化时间。

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

答案 2 :(得分: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,

答案 3 :(得分: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。

答案 4 :(得分:2)

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

这不是一个确切的解决方案,但它可能是一个可行的替代方案。这种方法存在一些风险。但如果真的有必要,你可能会走这条路。此方法使用未记录的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值。堆外意味着它不是垃圾收集的,所以你需要注意释放相关的内存。

以下代码基于此blog post about sun.misc.Unsafe

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

以下代码将打印来自单位化内存的一些随机双精度值。

Integer.MAX_VALUE

没有安全/范围检查,没有任何东西,你可以轻易地用它崩溃JVM,可能不适用于非SUN JRE,甚至可能在将来的SUN JRE版本中停止工作,但它可能是唯一的解决方案在某些情况下。它还可以分配&gt;与Java数组不同,java.nio.ByteBuffer.allocateDirect(...)大小的伪阵列。

ByteBuffer.allocateDirect(8*size).asDoubleBuffer()实际上在后台使用相同的Unsafe类来分配字节缓冲区,您可以使用DoubleBuffer使其适应ByteBuffer.allocateDirect(...),但 t.visit '/signup' t.fill_in 'user_email', :with => email t.fill_in 'user_password', :with => password t.fill_in 'user_password_confirmation', :with => password t.click_on 'Create Login' expect(page).to have_content('Welcome!') 仍然是用零初始化缓冲区,因此可能会产生性能开销。

答案 5 :(得分:1)

一旦声明“new double [n]”语句,数组就会初始化。没有办法解决它。

如果你为了优化而这样做,那么我会用你来过早优化。如果你的程序没有碰壁,那就不值得优化了。并且它不是你应该优化的阵列。

答案 6 :(得分:0)

如果您绝对需要像这样处理双数组,可以使用ArrayList来节省初始化时间,然后将其转换为数组:

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

来自Java docs:

  

toArray:以适当的顺序(从第一个元素到最后一个元素)返回一个包含此列表中所有元素的数组。

     

返回的数组将是“安全的”,因为没有对它的引用   由此列表维护。 (换句话说,这种方法必须分配一个   新数组,即使此列表由数组支持)。因此呼叫者   可以自由修改返回的数组。

     

此方法充当基于阵列和基于集合的桥梁   的API。

     

指定者:集合

中的toArray()

答案 7 :(得分: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++;
}

答案 8 :(得分:0)

我理解这个问题,真正的问题是分配一个巨大的两个维数组,如评论中所述

“static int [] [] lookup = new int [113088217] [2];不起作用,而private final static int [] [] lookup = new int [11308821] [2];(少10倍)进入不到一秒钟“

假设这是正确的,是的,对于大数字来说,这是骨头慢。你分配113088217 * 2 ints 的单块!您正在分配113088217单个阵列,它们是 Objects ,每个都必须在堆上分配:这意味着JVM正在寻找空间超过1亿次,标记它使用它,可能在内存变紧时运行GC等等......每个数组都占用大量(在这些巨大的数字中)额外的内存,加上每个内存包含2个整数。

对于这个巨大的案例:

1)切换索引,然后转到

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

不分配1.13亿个数组,而是分配 2个。在数组中执行查找时,请切换两个索引。

2)制作一维数组并自己进行查找

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

这需要进行简单的数学运算才能找到合适的索引。不是直接访问数组,而是编写一个函数来进行数学运算,调用代码应该使用它。