具有4gb以上元素的Java数组

时间:2009-05-18 15:26:48

标签: java arrays 64-bit

我有一个大文件,预计大约12 GB。我想将它全部加载到具有16 GB RAM的强大64位机器的内存中,但我认为Java不支持大字节数组:

File f = new File(file);
long size = f.length();
byte data[] = new byte[size]; // <- does not compile, not even on 64bit JVM

是否可以使用Java?

Eclipse编译器的编译错误是:

Type mismatch: cannot convert from long to int

javac给出:

possible loss of precision
found   : long
required: int
         byte data[] = new byte[size];

11 个答案:

答案 0 :(得分:20)

Java数组索引的类型为int(4个字节或32位),所以我担心你的数组中只有2个 31 - 1或2147483647个插槽。我将数据读入另一个数据结构,如2D数组。

答案 1 :(得分:13)

package com.deans.rtl.util;

import java.io.FileInputStream;
import java.io.IOException;

/**
 * 
 * @author william.deans@gmail.com
 *
 * Written to work with byte arrays requiring address space larger than 32 bits. 
 * 
 */

public class ByteArray64 {

    private final long CHUNK_SIZE = 1024*1024*1024; //1GiB

    long size;
    byte [][] data;

    public ByteArray64( long size ) {
        this.size = size;
        if( size == 0 ) {
            data = null;
        } else {
            int chunks = (int)(size/CHUNK_SIZE);
            int remainder = (int)(size - ((long)chunks)*CHUNK_SIZE);
            data = new byte[chunks+(remainder==0?0:1)][];
            for( int idx=chunks; --idx>=0; ) {
                data[idx] = new byte[(int)CHUNK_SIZE];
            }
            if( remainder != 0 ) {
                data[chunks] = new byte[remainder];
            }
        }
    }
    public byte get( long index ) {
        if( index<0 || index>=size ) {
            throw new IndexOutOfBoundsException("Error attempting to access data element "+index+".  Array is "+size+" elements long.");
        }
        int chunk = (int)(index/CHUNK_SIZE);
        int offset = (int)(index - (((long)chunk)*CHUNK_SIZE));
        return data[chunk][offset];
    }
    public void set( long index, byte b ) {
        if( index<0 || index>=size ) {
            throw new IndexOutOfBoundsException("Error attempting to access data element "+index+".  Array is "+size+" elements long.");
        }
        int chunk = (int)(index/CHUNK_SIZE);
        int offset = (int)(index - (((long)chunk)*CHUNK_SIZE));
        data[chunk][offset] = b;
    }
    /**
     * Simulates a single read which fills the entire array via several smaller reads.
     * 
     * @param fileInputStream
     * @throws IOException
     */
    public void read( FileInputStream fileInputStream ) throws IOException {
        if( size == 0 ) {
            return;
        }
        for( int idx=0; idx<data.length; idx++ ) {
            if( fileInputStream.read( data[idx] ) != data[idx].length ) {
                throw new IOException("short read");
            }
        }
    }
    public long size() {
        return size;
    }
}
}

答案 2 :(得分:6)

如果有必要,您可以将数据加载到一个数组数组中,这将为您提供最大的int.maxValue 平方字节,甚至比最强大的机器在内存中保存得更好。 / p>

答案 3 :(得分:2)

我建议你定义一些“块”对象,每个对象在数组中保存(比方说)1Gb,然后创建一个数组。

答案 4 :(得分:2)

不,数组由int索引(除了某些使用short s的JavaCard版本)。您需要将其分割成较小的数组,可能包含一个类型,为您提供get(long)set(long,byte)等。对于大的数据部分,您可能希望使用java映射文件。的NiO。

答案 5 :(得分:2)

您可以考虑使用FileChannel和MappedByteBuffer来存储映射文件,

FileChannel fCh = new RandomAccessFile(file,"rw").getChannel();
long size = fCh.size();
ByteBuffer map = fCh.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);

编辑:

好吧,我是个白痴,看起来像ByteBuffer只需要一个32位索引,这是奇怪的,因为FileChannel.map的size参数很长......但是如果你决定将文件分解成多个2Gb块用于加载我仍然建议使用内存映射IO,因为它可以带来非常大的性能优势。您基本上将所有IO责任转移到操作系统内核。

答案 6 :(得分:2)

不要用Integer.MAX_VALUE来限制自己

虽然很多年前这个问题已被提出过,但是我想参加一个简单的例子,只使用java se而不需要任何外部库

首先,让我们说它在理论上是不可能的,但实际上是可能的

新外观:如果数组是元素的对象,那么拥有一个数组数组的对象

这里是

的例子
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

/**
*
* @author Anosa
*/
 public class BigArray<t>{

private final static int ARRAY_LENGTH = 1000000;

public final long length;
private List<t[]> arrays;

public BigArray(long length, Class<t> glasss)
{
    this.length = length;
    arrays = new ArrayList<>();
    setupInnerArrays(glasss);

}

private void setupInnerArrays(Class<t> glasss)
{
    long numberOfArrays = length / ARRAY_LENGTH;
    long remender = length % ARRAY_LENGTH;
    /*
        we can use java 8 lambdas and streams:
        LongStream.range(0, numberOfArrays).
                        forEach(i ->
                        {
                            arrays.add((t[]) Array.newInstance(glasss, ARRAY_LENGTH));
                        });
     */

    for (int i = 0; i < numberOfArrays; i++)
    {
        arrays.add((t[]) Array.newInstance(glasss, ARRAY_LENGTH));
    }
    if (remender > 0)
    {
        //the remainer will 100% be less than the [ARRAY_LENGTH which is int ] so
        //no worries of casting (:
        arrays.add((t[]) Array.newInstance(glasss, (int) remender));
    }
}

public void put(t value, long index)
{
    if (index >= length || index < 0)
    {
        throw new IndexOutOfBoundsException("out of the reange of the array, your index must be in this range [0, " + length + "]");
    }
    int indexOfArray = (int) (index / ARRAY_LENGTH);
    int indexInArray = (int) (index - (indexOfArray * ARRAY_LENGTH));
    arrays.get(indexOfArray)[indexInArray] = value;

}

public t get(long index)
{
    if (index >= length || index < 0)
    {
        throw new IndexOutOfBoundsException("out of the reange of the array, your index must be in this range [0, " + length + "]");
    }
    int indexOfArray = (int) (index / ARRAY_LENGTH);
    int indexInArray = (int) (index - (indexOfArray * ARRAY_LENGTH));
    return arrays.get(indexOfArray)[indexInArray];
}

}

这是测试

public static void main(String[] args)
{
    long length = 60085147514l;
    BigArray<String> array = new BigArray<>(length, String.class);
    array.put("peace be upon you", 1);
    array.put("yes it worj", 1755);
    String text = array.get(1755);
    System.out.println(text + "  i am a string comming from an array ");

}

此代码仅受Long.MAX_VALUE和Java 限制,但您可以根据需要超出它(我将其设为3800 MB)

我希望这很有用,并提供一个简单的答案

答案 7 :(得分:1)

Java数组使用整数作为索引。因此,最大数组大小为Integer.MAX_VALUE。

(不幸的是,我无法从Sun那里找到任何关于此问题的证据,但plentydiscussions forums已提及此问题。)

我认为在此期间您可以做的最佳解决方案是制作2D数组,即:

byte[][] data;

答案 8 :(得分:1)

正如其他人所说,所有类型的所有Java数组都由int索引,因此可以是最大为2 31 - 1或2147483647个元素(~20亿) 。这由Java Language Specification指定,因此切换到另一个操作系统或Java虚拟机将无济于事。

如果你想编写一个类来克服这个,你可以使用一个数组(为了很大的灵活性)或更改类型(a long是8个字节,所以{{{ 1}}可以比long[]大8倍。

答案 9 :(得分:1)

java目前不支持超过2 ^ 32个元素的直接数组,

希望将来能看到java的这个功能

答案 10 :(得分:1)

我认为内存映射文件(使用CPU的虚拟内存硬件)的想法是正确的方法。除了MappedByteBuffer与本机数组具有相同的2Gb限制。这个人声称用MappedByteBuffer的一个非常简单的替代方法解决了这个问题:

http://nyeggen.com/post/2014-05-18-memory-mapping-%3E2gb-of-data-in-java/

https://gist.github.com/bnyeggen/c679a5ea6a68503ed19f#file-mmapper-java

不幸的是,当你阅读超过500Mb时,JVM会崩溃。