Java-массив с элементами более 4 ГБ

У меня большой файл, он должен быть около 12 ГБ. Я хочу загрузить все это в память на мускулистой 64-битной машине с 16 ГБ оперативной памяти, но я думаю, что 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];

Ответ 1

Индексы массива Java имеют тип int (4 байта или 32 бита), поэтому я боюсь, что вы ограничены 2 слотами 31 - 1 или 2147483647 в вашем массиве. Я бы прочитал данные в другой структуре данных, как 2D-массив.

Ответ 2

package com.deans.rtl.util;

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

/**
 * 
 * @author [email protected]
 *
 * 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;
    }
}
}

Ответ 3

При необходимости вы можете загрузить данные в массив массивов, который даст вам максимум байтов int.maxValue в квадрате, больше, чем даже самая жесткая машина будет хорошо храниться в памяти.

Ответ 4

Я предлагаю вам определить некоторые "блочные" объекты, каждый из которых содержит (скажем) 1Gb в массиве, а затем создает массив из них.

Ответ 5

Нет, массивы индексируются int (кроме некоторых версий JavaCard, которые используют short s). Вам нужно будет нарезать его на меньшие массивы, возможно, обертывая тип, который дает вам get(long), set(long,byte) и т.д. С большими объемами данных вы можете сопоставить файл с использованием java.nio.

Ответ 6

Вы можете использовать FileChannel и MappedByteBuffer для карты памяти,

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

Edit:

Хорошо, я идиот, похоже, что ByteBuffer использует только 32-разрядный индекс, который является нечетным, поскольку параметр размера FileChannel.map длинный... Но если вы решили разбить файл на несколько блоков 2Gb для загрузки. Я по-прежнему рекомендую отображать IO с памятью, так как могут быть довольно большие преимущества в производительности. Вы в основном переносите всю ответственность ввода-вывода на ядро ​​ОС.

Ответ 7

Массивы Java используют целые числа для своих индексов. В результате максимальный размер массива - Integer.MAX_VALUE.

(К сожалению, я не могу найти никаких доказательств от самих Sun, но есть много обсуждения на их forums об этом уже.)

Я думаю, что лучшим решением, которое вы могли бы сделать в это время, было бы создание 2D-массива, т.е.:

byte[][] data;

Ответ 8

Как отмечали другие, все массивы Java всех типов индексируются с помощью int и поэтому могут иметь максимальный размер 2 31 - 1 или 2147483647 элементов (~ 2 миллиарда). Это указано Спецификация Java Language, поэтому переход на другую операционную систему или виртуальную машину Java не поможет.

Если вы хотите написать класс для преодоления этого, как было предложено выше, вы могли бы использовать массив массивов (для большой гибкости) или типов изменений (a long - 8 байт, поэтому long[] может в 8 раз больше, чем byte[]).

Ответ 9

Я думаю, что идея сопоставления памяти с файлом (с использованием аппаратного обеспечения виртуальной памяти процессора) является правильным подходом. За исключением того, что 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

К сожалению, JVM падает, когда вы читаете за пределами 500 МБ.

Ответ 10

не ограничивайте себя с помощью 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 МБ)

Я надеюсь, что это полезно и даст простой ответ

Ответ 11

java не поддерживает прямой массив с более чем 2 ^ 32 элементами в настоящее время,

надеюсь увидеть эту функцию java в будущем