具有超过4GB元素的Java数组

我有一个大文件,预计大约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]; 

Java数组索引的types是int (4个字节或32位),所以恐怕只能在数组中使用2 31 – 1或2147483647个插槽。 我会读取数据到另一个数据结构,如二维数组。

 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; } } } 

如果有必要的话,你可以把数据加载到一个数组数组中,这会给你最大的int.maxValue 平方的字节数,甚至比最牛的机器在内存中保持的好。

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

不,数组通过int s被索引(除了某些使用short s的JavaCard版本)。 你将需要把它分成更小的数组,可能包装在一个types,让你get(long)set(long,byte)等数据部分大,你可能想映射文件使用Java。 NIO。

您可能会考虑使用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的大小参数是一个很长的…但是,如果你决定把文件拆分成多个2Gb块加载我仍然build议内存映射IO,因为可以有相当大的性能优势。 你基本上将所有的IO职责移到OS内核上。

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

(不幸的是,我无法从Sun自己那里find任何证据,但是在他们的论坛上已经有很多 讨论了。

我认为在此期间你可以做的最好的解决scheme是制作二维数组,即:

 byte[][] data; 

正如其他人所说,所有types的所有Java数组都被int索引,所以可以是最大大小2 31 – 1,或者2147483647元素(大约20亿)。 这是由Java语言规范指定的,因此切换到其他操作系统或Java虚拟机将无济于事。

如果你想写一个类来克服这个问题,就像上面所提到的那样,你可以使用一个数组数组(为了很大的灵活性)或者改变types(一个long是8个字节, long[]可以是8个一个byte[] )。

不要用Integer.MAX_VALUE来限制你的自我

虽然这个问题在很多年前就已经被提出过了,但是ai想要用一个简单的例子来参与,只使用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]; } 

}

这是testing

 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)

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

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

希望将来能看到java的这个特性

我认为使用内存映射文件(使用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崩溃。