Efficiënt een groot bestand lezen met Java

1. Overzicht

Deze tutorial zal laten zien hoe je alle regels uit een groot bestand in Java kunt lezen op een efficiënte manier.

Dit artikel maakt deel uit van de “Java - Terug naar Basic”Tutorial hier op Baeldung.

2. Voorlezen in het geheugen

De standaardmanier om de regels van het bestand te lezen is in het geheugen - zowel Guava als Apache Commons IO bieden een snelle manier om precies dat te doen:

Files.readLines (nieuw bestand (pad), Charsets.UTF_8);
FileUtils.readLines (nieuw bestand (pad));

Het probleem met deze benadering is dat alle bestandsregels in het geheugen worden bewaard - wat snel zal leiden tot Onvoldoende geheugen fout als het bestand groot genoeg is.

Bijvoorbeeld - het lezen van een ~ 1Gb-bestand:

@Test openbare leegte gegevenUsingGuava_whenIteratingAFile_thenWorks () gooit IOException {String path = ... Files.readLines (nieuw bestand (pad), Charsets.UTF_8); }

Dit begint met een kleine hoeveelheid geheugen die wordt verbruikt: (~ 0 Mb verbruikt)

[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Totaal geheugen: 128 Mb [main] INFO org.baeldung.java.CoreJavaIoUnitTest - Vrij geheugen: 116 Mb

Echter, nadat het volledige bestand is verwerkthebben we aan het einde: (~ 2 Gb verbruikt)

[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Totaal geheugen: 2666 Mb [main] INFO org.baeldung.java.CoreJavaIoUnitTest - Vrij geheugen: 490 Mb

Wat betekent dat ongeveer 2,1 Gb geheugen wordt verbruikt door het proces - de reden is simpel - de regels van het bestand worden nu allemaal in het geheugen opgeslagen.

Op dit punt zou het duidelijk moeten zijn dat Door de inhoud van het bestand in het geheugen te bewaren, wordt het beschikbare geheugen snel uitgeput - ongeacht hoeveel dat werkelijk is.

Bovendien, we hebben meestal niet alle regels in het bestand in het geheugen tegelijk nodig - in plaats daarvan moeten we ze allemaal kunnen doorlopen, wat bewerkingen uitvoeren en het weggooien. Dit is dus precies wat we gaan doen - de regels doorlopen zonder ze allemaal in het geheugen vast te houden.

3. Streamen via het bestand

Laten we nu naar een oplossing kijken - we gaan een java.util.Scanner om de inhoud van het bestand te doorlopen en regels serieel één voor één op te halen:

FileInputStream inputStream = null; Scanner sc = null; probeer {inputStream = nieuwe FileInputStream (pad); sc = nieuwe scanner (inputStream, "UTF-8"); while (sc.hasNextLine ()) {String line = sc.nextLine (); // System.out.println (regel); } // merk op dat Scanner uitzonderingen onderdrukt als (sc.ioException ()! = null) {throw sc.ioException (); }} eindelijk {if (inputStream! = null) {inputStream.close (); } if (sc! = null) {sc.close (); }}

Deze oplossing zal alle regels in het bestand doorlopen - waardoor elke regel kan worden verwerkt - zonder verwijzingen ernaar te behouden - en tot slot, zonder ze in het geheugen te bewaren: (~ 150 Mb verbruikt)

[main] INFO org.baeldung.java.CoreJavaIoUnitTest - Totaal geheugen: 763 Mb [main] INFO org.baeldung.java.CoreJavaIoUnitTest - Vrij geheugen: 605 Mb

4. Streamen met Apache Commons IO

Hetzelfde kan worden bereikt met behulp van de Commons IO-bibliotheek, door de gewoonte LineIterator verstrekt door de bibliotheek:

LineIterator it = FileUtils.lineIterator (theFile, "UTF-8"); probeer {while (it.hasNext ()) {String line = it.nextLine (); // doe iets met line}} tenslotte {LineIterator.closeQuietly (it); }

Omdat niet het hele bestand volledig in het geheugen is opgeslagen, zal dit ook resulteren in vrij conservatieve cijfers over geheugengebruik: (~ 150 Mb verbruikt)

[main] INFO o.b.java.CoreJavaIoIntegrationTest - Totaal geheugen: 752 Mb [main] INFO o.b.java.CoreJavaIoIntegrationTest - Vrij geheugen: 564 Mb

5. Conclusie

Dit korte artikel laat zien hoe lijnen in een groot bestand verwerken zonder iteratief, zonder het beschikbare geheugen uit te putten - wat erg handig blijkt bij het werken met deze grote bestanden.

De implementatie van al deze voorbeelden en codefragmenten is te vinden in ons GitHub-project - dit is een op Maven gebaseerd project, dus het zou gemakkelijk moeten kunnen worden geïmporteerd en uitgevoerd zoals het is.