Hvordan lese en stor fil effektivt med Java

1. Oversikt

Denne opplæringen vil vises hvordan du leser alle linjene fra en stor fil i Java på en effektiv måte.

Denne artikkelen er en del avJava - Tilbake til Basic”Tutorial her på Baeldung.

2. Lesing i minnet

Standard måten å lese linjene i filen er i minnet - både Guava og Apache Commons IO gir en rask måte å gjøre nettopp det:

Files.readLines (ny fil (sti), Charsets.UTF_8);
FileUtils.readLines (ny fil (bane));

Problemet med denne tilnærmingen er at alle fillinjene holdes i minnet - noe som raskt vil føre til OutOfMemoryError hvis filen er stor nok.

For eksempel - leser en ~ 1 GB fil:

@Test offentlig ugyldighet givenUsingGuava_whenIteratingAFile_thenWorks () kaster IOException {Stringsti = ... Files.readLines (ny fil (sti), Charsets.UTF_8); }

Dette starter med at en liten mengde minne blir brukt: (~ 0 Mb forbrukt)

[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Totalt minne: 128 Mb [main] INFO org.baeldung.java.CoreJavaIoUnitTest - Gratis minne: 116 Mb

Derimot, etter at hele filen er behandlet, har vi på slutten: (~ 2 GB forbrukt)

[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Totalt minne: 2666 Mb [main] INFO org.baeldung.java.CoreJavaIoUnitTest - Gratis minne: 490 Mb

Noe som betyr at omtrent 2,1 GB minne forbrukes av prosessen - årsaken er enkel - linjene i filen lagres alle i minnet nå.

Det bør være åpenbart ved dette punktet at å lagre innholdet i filen vil raskt tømme det tilgjengelige minnet - uavhengig av hvor mye det faktisk er.

Hva mer, vi trenger vanligvis ikke alle linjene i filen i minnet på en gang - i stedet trenger vi bare å kunne gjenta gjennom hver enkelt, gjøre litt behandling og kaste den. Så dette er nøyaktig hva vi skal gjøre - gå gjennom linjene uten å holde dem alle i minnet.

3. Streaming gjennom filen

La oss nå se på en løsning - vi skal bruke en java.util.Scanner for å kjøre gjennom innholdet i filen og hente linjer serievis, en etter en:

FileInputStream inputStream = null; Skanner sc = null; prøv {inputStream = ny FileInputStream (sti); sc = ny skanner (inputStream, "UTF-8"); mens (sc.hasNextLine ()) {Strenglinje = sc.nextLine (); // System.out.println (linje); } // merk at skanneren undertrykker unntak hvis (sc.ioException ()! = null) {throw sc.ioException (); }} til slutt {if (inputStream! = null) {inputStream.close (); } hvis (sc! = null) {sc.close (); }}

Denne løsningen vil gjentas gjennom alle linjene i filen - muliggjør behandling av hver linje - uten å beholde referanser til dem - og avslutningsvis, uten å ha dem i minnet: (~ 150 Mb forbrukt)

[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Totalt minne: 763 Mb [main] INFO org.baeldung.java.CoreJavaIoUnitTest - Gratis minne: 605 Mb

4. Streaming med Apache Commons IO

Det samme kan oppnås ved å bruke Commons IO-biblioteket ved å bruke tollen LineIterator levert av biblioteket:

LineIterator it = FileUtils.lineIterator (theFile, "UTF-8"); prøv {while (it.hasNext ()) {Strenglinje = it.nextLine (); // gjør noe med linje}} til slutt {LineIterator.closeQuietly (it); }

Siden hele filen ikke er fullt i minnet - vil dette også resultere i ganske konservative tall for minneforbruk: (~ 150 Mb forbrukt)

[main] INFO o.b.java.CoreJavaIoIntegrationTest - Totalt minne: 752 Mb [main] INFO o.b.java.CoreJavaIoIntegrationTest - Gratis minne: 564 Mb

5. Konklusjon

Denne raske artikkelen viser hvordan du gjør det behandle linjer i en stor fil uten iterativt, uten å tømme det tilgjengelige minnet - som viser seg ganske nyttig når du arbeider med disse store filene.

Implementeringen av alle disse eksemplene og kodebiter kan du finne i GitHub-prosjektet vårt - dette er et Maven-basert prosjekt, så det skal være enkelt å importere og kjøre som det er.


$config[zx-auto] not found$config[zx-overlay] not found