2009-05-18 11 views
17

Ho un file di grandi dimensioni, è previsto in circa 12 GB. Voglio caricare tutto in memoria su un computer a 64 bit robusto con 16 GB di RAM, ma credo che Java non supporta array di byte che grande:Array Java con più di 4 gb elementi

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

E 'possibile con Java?

L'errore di compilazione dal compilatore Eclipse è:

Type mismatch: cannot convert from long to int 

javac dà:

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

Solo curioso: perché è necessario conservare così tanti dati nella memoria allo stesso tempo? Non sarebbe possibile dividerlo in pezzi? –

+1

+1 al commento di Bruno. L'unico modo in cui avere l'intero file in memoria sarà un vantaggio è se devi fare accessi casuali in punti diversi del file, e in questo caso è quasi sicuramente meglio analizzarlo in una rappresentazione più computabile – kdgregory

+0

I am andando a cercare di usare un albero prefissato (trie) per mantenere i dati, questo potrebbe ridurlo abbastanza da inserirsi in 2GB di memoria. –

risposta

18

indici di array Java sono di tipo int (4 byte o 32 bit), quindi temo sei limitato a 2 - 1 o 2147483647 slot nell'array. Leggevo i dati in un'altra struttura dati, come un array 2D.

+0

grazie a tutti. Bill ha avuto la risposta per prima. –

+0

@OmryYadan, Il [limite reale sarà effettivamente inferiore] (http://stackoverflow.com/questions/3038392/do-java-arrays-have-a-maximum-size/8381338#comment45805541_3039805) rispetto a 2147483647. – Pacerier

+0

intendi MAX_INT - 8? https: // github.com/omry/banana/blob/1621638d6eb4db773045af66eac66be0fffa91fa/banana/src/net/yadan/banana/memoria/blocco/BigBlockAllocator.java # L25 –

6

Se necessario, è possibile caricare i dati in un array di array, che vi darà un massimo di int.maxValue quadrato byte, più che anche la macchina beefiest terrebbe bene nella memoria.

+0

questo sarebbe il mio prossimo passo. dato che ho intenzione di fare una ricerca binaria sui dati, andrà a rovinare il codice, ma temo che non ci sia scelta. –

+0

È possibile creare una classe che gestisca un array di matrici, ma fornisce un'astrazione simile a un array normale, ad es. Con get e set che prendono un indice lungo. –

2

Suggerisco di definire alcuni oggetti "di blocco", ognuno dei quali contiene (diciamo) 1 Gb in una matrice, quindi ne crea una matrice.

1

Gli array Java utilizzano numeri interi per i relativi indici. Di conseguenza, la dimensione massima dell'array è Integer.MAX_VALUE.

(Purtroppo, non riesco a trovare alcuna prova da parte di Sun se stessi su questo, ma ci sono plenty di discussions sul loro forums su di esso già.)

Penso che la soluzione migliore che si poteva fare nel frattempo sarebbe quella di fare una matrice 2D, cioè:

byte[][] data; 
2

No, gli array sono indicizzati da int s (tranne alcune versioni di JavaCard che utilizzano short s). Sarà necessario suddividerlo in array più piccoli, probabilmente inserendo un tipo che ti dà get(long), set(long,byte), ecc. Con sezioni di dati così grandi, potresti voler mappare il file usando java.nio.

1

Come altri hanno detto, tutti gli array Java di tutti i tipi sono indicizzate da int, e quindi possono essere di dimensioni max 2 -1 o 2147483647 elementi (~ 2 miliardi). Questo è specificato dallo Java Language Specification, quindi passare a un altro sistema operativo o Java Virtual Machine non sarà di aiuto.

Se si voleva scrivere una classe per superare questo come suggerito sopra si potrebbe, che potrebbe utilizzare un array di array (per un sacco di flessibilità) o modificare i tipi (un long è di 8 byte in modo da un long[] può essere 8 volte più grande di un byte[]).

2

Si potrebbe considerare l'utilizzo FileChannel e MappedByteBuffer alla memoria mappare il file,

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

Edit:

Ok, sono un idiota sembra ByteBuffer richiede solo un indice a 32 bit, nonché il che è strano dato che il parametro size su FileChannel.map è un lungo ...Ma se decidi di suddividere il file in più blocchi da 2 Gb per il caricamento, ti consiglio comunque l'I/O mappato in memoria in quanto possono esserci notevoli vantaggi in termini di prestazioni. In pratica stai spostando tutta la responsabilità di IO sul kernel del sistema operativo.

+0

Ho anche raggiunto la stessa limitazione di 'ByteBuffer' che, a mio avviso, dovrebbe essere in grado di gestire offset e indici lunghi almeno a livello di interfaccia. L'implementazione concreta dovrebbe verificare gli intervalli in modo esplicito. Sfortunatamente non è possibile mappare più di 2 GB di file in memoria. –

+0

Evidenzia come questo è il modo giusto per andare, anche se devi dividere i dati in blocchi 2G - avvolgere i blocchi in una classe che indicizza con un lungo se lo desideri. –

+0

MappedByteBuffer ha anche un limite di 2 GB, praticamente inutile. Vedi http://nyeggen.com/post/2014-05-18-memory-mapping-%3E2gb-of-data-in-java/ per una soluzione che chiama i metodi interni JNI per risolvere il problema. – AqD

12
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; 
    } 
} 
} 
+0

Una buona idea per implementare il proprio ByteArray per risolvere questo caso. Se non fosse stato per la tua risposta, probabilmente non avrei pensato di farlo. – UnixShadow

+0

A qualcuno interessa aggiungere un metodo di aggiornamento (byte [] b, int start, int size)? :) – rogerdpack

0

Java non supporta allineamento diretto con più di 2^32 elementi Attualmente i,

speranza di vedere questa caratteristica di Java in futuro

+0

No, il limite è 2^31 - 1 elementi. E la tua seconda riga non cita alcun riferimento. – Nayuki

1

Credo che l'idea della memoria a mappare il file (usando l'hardware della memoria virtuale della CPU) è l'approccio giusto. Tranne che MappedByteBuffer ha la stessa limitazione di 2 Gb come array nativi. Questo ragazzo sostiene di aver risolto il problema con un piuttosto semplice alternativa al MappedByteBuffer:

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

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

Purtroppo la JVM si blocca quando si legge al di là di 500Mb.

+0

Mentre in questo specifico esempio il mio caso d'uso era leggere un file, questo non è l'unico caso d'uso per array di grandi dimensioni. –

1

non limitano la vostra auto con Integer.MAX_VALUE

anche se questa domanda è stato chiesto molti anni fa, ma ai volevano partecipare con un semplice esempio utilizzando solo Java SE senza librerie esterne

dapprima diciamo che è teoricamente possibile, ma praticamente possibile

un aspetto nuovo: se l'array è un oggetto di elementi quello di avere un oggetto che è array di array

Ecco l'esempio

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

}

ed ecco il test

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 "); 

} 

questo codice è limitato solo dalla soltanto Long.MAX_VALUE e Java mucchio ma si può superare come si voglio (ho fatto 3800 MB)

spero che questo sia utile e fornisca una risposta semplice

+1

da allora ho scritto Banana: https://github.com/omry/banana, una lib che ti permette di farlo tra le altre cose. –

+0

@OmryYadan buon lavoro ho dato un'occhiata ad alcuni esempi good bro (: - – Anas

+0

Il wiki è molto carino https://github.com/omry/banana/wiki/Block-Allocators –

Problemi correlati