Java 如何避免初始化大型阵列

Java 如何避免初始化大型阵列,java,arrays,Java,Arrays,我分配了大量的double作为 double[] x = new double[ n ]; 其中n很大,我希望避免初始化以节省时间。有可能吗?简短回答:没有。数组在创建时总是被调零 如果你的分析显示这是一个主要的瓶颈,你可以考虑保持一组数组实例,每个集合的长度都要大于 N< /代码>。问题是,您可能需要一个包装器对象来包含数据数组和使用的实际长度,因为您不能再使用数据。length您可以使用数组列表或其他东西,并根据需要向其中添加元素来构建数组吗?这将节省初始化时间,如果这是您的问题 Arra

我分配了大量的double作为

double[] x = new double[ n ];

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

简短回答:没有。数组在创建时总是被调零


如果你的分析显示这是一个主要的瓶颈,你可以考虑保持一组数组实例,每个集合的长度都要大于<代码> N< /代码>。问题是,您可能需要一个包装器对象来包含数据数组和使用的实际长度,因为您不能再使用

数据。length

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

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

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


如果您这样做是为了优化,那么我请您阅读有关过早优化的内容。如果你的程序没有撞到墙,那么它就不值得优化。从防御性角度讲,它也不是您应该优化的阵列。

您可以使用ArrayList来节省初始化时间,如果您确实需要使用双阵列,则可以将其转换为阵列,如下所示:

List<Double> withoutInitializing = new ArrayList<Double>();
Double[] nowYouConvert = (Double[]) withoutInitializing.toArray();
List withoutinitialize=newarraylist();
Double[]nowYouConvert=(Double[]),而不初始化.toArray();
来自Java文档:

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

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

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

由集合中的:toArray()指定


有关如何不初始化数组的一些解决方法

创建一个保证大于最大可能条目数的数组,并对其进行部分填充

例如,您可以决定用户永远不会提供超过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
始终包含正确的元素计数。 下面的代码段显示了如何将数字读入部分填充的 数组


正如其他人已经提到的,简单的答案是:不,您无法避免初始化部分。除非您使用的是一些
本机
分配或使用created作为的视图,否则当且仅当字节缓冲区本身是直接的时,将是直接的

如果您没有使用其中任何一个,那么为了尽快分配和初始化阵列,您需要最小化
GC
调用,并且必须确保JVM具有存储和使用该阵列所需的内存

在Albert Hendriks的例子中:
static int[][]lookup=new int[113088217][2]
,如果没有至少2.3G(12+113088217*(12+2*4)字节)的内存,JVM将无法分配所需的空间。请注意,我没有添加所需的填充空间(内存对齐)


回答为什么
lookup=newint[2*113088217]执行速度更快。这是因为处理的内存要少得多,因为我们没有子数组(头+元素+每个子数组的对齐方式),只需要(2*113088217*4+12)字节=~804M。

如果您不想初始化太长的数组,您可以为数组大小确定一个限制,这不会让您等待太久。我建议将长数组拆分为更小的数组。 定义一个保存数组的列表。如果数组已填充,请将其添加到列表中。然后继续填充一个新的

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;
    }
}
import java.util.ArrayList;
导入java.util.List;
公共类测试员{
专用静态最终整数限值=30;
私有静态int索引=0;
私有静态int[][]查找;
私有静态列表=新的ArrayList();
公共静态void main(字符串[]args){
查找=新整数[限值][1];

对于(int i=0;i***警告***不安全替代方案**

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

SuperDoubleArray sda = new SuperDoubleArray(100);
for (int i=0; i<sda.size(); i++) {
    System.out.println(sda.get(i));
}
sda.deallocate();
下面的代码基于此

下面的代码将打印来自统一化内存的一些随机双精度值

SuperDoubleArray sda = new SuperDoubleArray(100);
for (int i=0; i<sda.size(); i++) {
    System.out.println(sda.get(i));
}
sda.deallocate();
SuperDoubleArray sda=新的SuperDoubleArray(100);
对于(int i=0;i
Integer.MAX_VALUE
大小的伪数组,与Java数组不同


java.nio.ByteBuffer.allocateDirect(…)
实际上在幕后使用同一个不安全的类来分配字节缓冲区,您可以使用
ByteBuffer.allocateDirect(8*size).asDoubleBuffer()
使其适应
DoubleBuffer
,但
ByteBuffer.allocateDirect(…)
仍在用零初始化缓冲区,因此可能会有性能开销。

据我所知,真正的问题是分配一个巨大的二维数组,如注释中所述

“static int[]lookup=new int[113088217][2];不起作用,而private final static int[]
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();