10

我分配了大量的双打作为

double[] x = new double[ n ];

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

4

9 回答 9

11

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

如果您的分析表明这是一个主要瓶颈,您可以考虑保留一个数组实例池,每个集合的长度比n以往任何时候都大。问题是您可能需要一个包装器对象来包含数据数组和使用的实际长度,因为您不能再使用data.length.

于 2012-04-10T22:27:46.270 回答
9

您可以使用一个ArrayList或其他东西,并根据需要向其中添加元素来构建您的数组吗?如果这是您的问题,这将节省初始化时间。

ArrayList<double> x = new ArrayList<double>();
于 2012-04-10T22:34:38.483 回答
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,

于 2016-01-02T23:04:52.723 回答
3

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

这不是一个精确的解决方案,但它可能是一个可行的替代方案。这种方法有一些风险。但如果真的绝对必要,你可能会走这条路。此方法使用未记录的sun.misc.Unsafe类来分配堆外内存来存储双精度值。堆外意味着它不是垃圾收集的,因此您需要注意释放关联的内存。

以下代码基于有关 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);
    }

}

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

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 版本中停止工作,但在某些情况下它可能是唯一的解决方案. 与 Java 数组不同,它还可以分配 >Integer.MAX_VALUE大小的伪数组。

java.nio.ByteBuffer.allocateDirect(...)实际上在幕后使用相同的 Unsafe 类来分配字节缓冲区,您可以使用ByteBuffer.allocateDirect(8*size).asDoubleBuffer()它来适应 a DoubleBuffer,但ByteBuffer.allocateDirect(...)仍在用零初始化缓冲区,因此它可能会产生性能开销。

于 2016-01-07T02:01:56.437 回答
2

就像其他人已经提到的那样,简单的答案是:不,您无法避免初始化部分。除非您正在使用某些native分配或使用IntBuffer创建为字节缓冲区的视图,否则当且仅当字节缓冲区本身是直接的时,它才会是直接的。

如果您不使用它们中的任何一个,那么为了尽快分配和初始化数组,您需要最小化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) bytes=~804M。

于 2016-01-02T23:01:56.133 回答
1

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

如果您这样做是为了优化,那么我会雇用您阅读有关过早优化的信息。如果您的程序没有碰壁,那么它不值得优化。而且它也绝对不是您应该优化的阵列。

于 2012-04-10T22:40:11.867 回答
1

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

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

来自 Java 文档:

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

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

此方法充当基于数组和基于集合的 API 之间的桥梁。

指定者:集合中的 toArray()

于 2016-01-02T20:53:39.847 回答
0

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

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

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

对于这个巨大的案例:

1)切换索引,然后去

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

这不是分配 1.13 亿个数组,而是分配两个. 在数组中进行查找时,切换两个索引。

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

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

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

于 2016-01-07T02:35:42.840 回答
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++;
}
于 2016-01-02T22:32:27.537 回答