2015-11-04 24 views
5

Ho un PC con 4 GB di RAM e un file con 10 GB di memoria. Ora voglio verificare, se ogni riga del file è unica così ho scritto il seguente codice:Come estrarre righe univoche nel file> 10 GB con 4 GB di RAM

import java.io.BufferedReader; 
import java.io.File; 
import java.io.FileReader; 
import java.io.FileWriter; 
import java.io.IOException; 
import java.util.HashSet; 
import java.util.Set; 

public class Cleaner { 

    public static void main(String[] args) throws IOException { 
     if (args.length < 2) { 
      System.out.println("Too less parameters!"); 
      return; 
     } 

     File file = new File(args[0]); 
     BufferedReader buff = new BufferedReader(new FileReader(file)); 
     String line; 
     Set<String> set = new HashSet<String>(); 
     while ((line = buff.readLine()) != null) { 
      set.add(line); 
     } 
     FileWriter fw = new FileWriter(args[1]); 
     for (String s : set) { 
      fw.write(s + "\n"); 
      fw.flush(); 
     } 
     fw.close(); 
     buff.close(); 

    } 

} 

Ma ho un OutOfMemoryException quindi la mia domanda è:
Come dovrei cambiare il mio codice per ottenere un file in cui ogni riga è unica?
Grazie per il vostro aiuto in anticipo.

+0

Dividere in blocchi e confrontare paiawaise. Oppure cancella ogni riga e archivia l'hash insieme alla linea. – user

+0

Il problema con l'hashing è che ogni riga è solo un hash e come posso chunk mi mancherà forse alcune linee duplicate. –

+0

Dai un'occhiata a RandomAccessFile puoi leggere la riga 1 da RandomAccessFile 'a' e confrontarla con tutte le altre linee di RandomAccessFile 'b'. Dopo di ciò leggere la riga 2 e così via – user

risposta

0

Si potrebbe provare a cercare la linea duplicata hash primo a identificare potenziali linee duplicate:

Map<Integer, Integer> hashes = new HashMap<>(); 
Map<Integer, Integer> dupes = new HashMap<>(); 
int i = 0; 
while ((line = buff.readLine()) != null) { 
    int hash = line.hashCode(); 
    Integer previous = hashes.get(hash); 
    if (previous != null) { //potential duplicate 
    dupes.put(i, previous); 
    } else { 
    hashes.put(hash, i); 
    } 
    ++i; 
} 

Alla fine si dispone di un elenco di potenziali duplicati. Se dupes è vuoto non c'era duplicato, se non lo è, puoi fare un secondo passaggio sul file per verificare se le linee sono veramente identiche.

+1

Sarebbe anche molto più efficiente in termini di memoria usare Koloboke ['IntIntMap'] (http://openhft.github.io/Koloboke/api/0.6/java7/net/openhft/koloboke/collect/map/IntIntMap.html) o Trove ['TIntIntHashMap'] (http://trove4j.sourceforge.net/javadocs/gnu/trove/map/hash/TIntIntHashMap.html) per rappresentare la mappa. –

+0

A proposito di quel secondo passaggio, puoi saltarlo se passi ai file abilitati all'accesso casuale. Quindi puoi semplicemente scorrere indietro e controllare la linea su ciascun potenziale duplicato. In realtà, il secondo passaggio sarebbe impossibile senza accesso casuale comunque. – bezmax

+0

@bezmax L'accesso casuale non può aiutarti a passare alla riga xyz - puoi solo saltare un numero di byte - nel mio esempio potrei memorizzare la posizione dei byte invece del numero di riga. – assylias

0

Non è possibile eseguire questa operazione in questo modo a causa della memoria RAM. Invece, puoi leggere il file e generare n file con una dimensione fissa (es: 10.000 linee), leggere una riga e inserirla nel file attuale. Quando raggiungi il limite del file, aprine uno nuovo, rilascia tutti gli oggetti per il salvataggio della memoria, quindi esegui un secondo ciclo e confronta ogni riga del file originale usando una stringa (per la linea) con i n file generati. Forse in questo modo puoi evitare il vuoto di memoria.

È un po 'strano e sarà un processo lento, ma in questo modo penso che tu possa raggiungere le tue esigenze.

Se hai bisogno di codice, fammi sapere.

Speranza aiuta

+0

Si sa che non è necessario caricare l'intero file in memoria per elaborarlo vero? – Marco

-1

si può barare con qualcosa di simile: (esempio è Groovy, ma l'equivalente Java avrebbe funzionato)

def hashes = [] 
def writer = new PrintWriter(new FileWriter("out.txt")) 
new File('test.txt').eachLine { line -> 
    def hashCode = DigestUtils.sha256Hex(line) //Commons digest library 
    if (!(hashCode in hashes)) { 
     hashes << hashCode 
     writer.println(line) 
    } 
} 
writer.close() 

che non dovrebbe richiedere più di circa 1 GB di RAM per funzionare. Gli hash SHA256 ti daranno probabilmente più certezza sull'unicità di una linea rispetto al metodo standard hashCode.

+1

Questo non funzionerà a causa di collisioni hash. – bezmax

+0

Si suppone che rilevi le collisioni hash perché voleva un file che avesse solo linee univoche. Fammi indovinare, tu sei il ragazzo che ha downvoted questo non sei tu ... –