Java MappedByteBuffer Example
In this post, we are going to discuss about the class java.nio.MappedByteBuffer
1. MappedByteBuffer Class
There are two ways for reading a file, sequentially and randomly. Files that can be explored sequentially are known as sequential files. Files that permit random access to their contents are known as random access files (RAFs). Sequential files are used more often because they are easy to create, but RAFs are more flexible and their data can be located faster.
With a RAF, you can open the file, seek a particular location, and read from or write to that file. After you open a RAF, you can read from it or write to it in a random manner just by using a record number, or you can add to the beginning or end of the file since you know how many records are in the file. A RAF allows you to read a single character, read a chunk of bytes or a line, replace a portion of the file, append lines, delete lines, and so forth, and allows you to perform all of these actions in a random manner.
FileChannel
was introduced in Java 4, but recently it was updated to implement the new SeekableByteChannel
interface, combining their forces to achieve more power. SeekableByteChannel provides the random access file feature, while FileChannel offers great advanced features such as mapping a region of the file directly into memory for faster access and locking a region of the file.
One of the great FileChannel facilities is the capability to map a region of a channel’s file directly into memory. This is possible thanks to the FileChannel.map()
method. The map() method will return a MappedByteBuffer that actually represents the extracted region.
Before going deeper into the MappedByteBuffer class, let’s talk about a little some important topics when working with random access files (RAFs).
1.1 What is a buffer?
A buffer is essentially an array (usually of bytes) that holds some data to be written or that was just read (Java has the ByteBuffer
class).
Three are the essential properties of a buffer:
- Limit – When writing from a buffer, the limit specifies how much data remains to get.
- Position – The position keeps track of how much data you have read or written.
- Capacity – The capacity specifies the maximum amount of data that can be stored in a buffer.
1.2 What is a channel?
In a stream-oriented I/O system, an input stream produces 1 byte of data and an output stream consumes 1 byte of data. By contrast, in a block-oriented I/O system, the input/output stream produces or consumes a block of data in one step.
Channels are analogous to streams, but with a few differences:
- While streams are typically one-way (read or write), channels support read and write.
- Channels can be read and written asynchronously.
- Channels always read to, or write from, a buffer. All data that is sent to a channel must first be placed in a buffer. Any data that is read from a channel is read into a buffer.
The new SeekableByteChannel interface provides support for RAF by implementing the notion of position over channels. We can read or write a ByteBuffer from or to a channel, get or set the current position, and truncate an entity connected to a channel to a specified dimension.
Let’s see an example of using the MappedByteBuffer
Class file.
2. Executing some code
App.java
package com.javacodegeeks.examples.mappedbytebuffer; import java.io.File; import java.io.IOException; import java.nio.CharBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.EnumSet; import java.util.logging.Level; import java.util.logging.Logger; public class App { private static final Logger logger = Logger.getLogger("App"); private static final String FILE_READ = "rafRead.txt"; //= Change this according to your needs ========================= private static final String FILE_WRITE = "C:\\Tmp\\rafWrite.txt"; // ======================================================================== // = Utility function to get a file located in the classpath ============== // ======================================================================== public static Path getFileURIFromClasspath(String fileName) throws Exception { Path result = null; String classpath = System.getProperty("java.class.path"); result = FileSystems.getDefault().getPath(classpath + File.separator + fileName); return result; } public static void main(String[] args) { CharBuffer charBuffer = null; String charEncoding = null; MappedByteBuffer mappedByteBuffer = null; try { charEncoding = System.getProperty("file.encoding"); // Read a file Path pathRead = App.getFileURIFromClasspath(App.FILE_READ); if (Files.exists(pathRead, new LinkOption[]{LinkOption.NOFOLLOW_LINKS})) { try (FileChannel fileChannel = (FileChannel) Files.newByteChannel(pathRead, EnumSet.of(StandardOpenOption.READ))) { mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size()); if (mappedByteBuffer != null) { logger.info("Reading file..."); charBuffer = Charset.forName(charEncoding).decode(mappedByteBuffer); logger.info("File content: " + charBuffer.toString()); } } catch (IOException ioe) { logger.log(Level.SEVERE, ioe.getMessage()); ioe.printStackTrace(); } } // Write a file Path pathWrite = FileSystems.getDefault().getPath(App.FILE_WRITE); if (Files.notExists(pathWrite, new LinkOption[]{LinkOption.NOFOLLOW_LINKS})) { Files.createFile(pathWrite); try (FileChannel fileChannel = (FileChannel) Files.newByteChannel(pathWrite, EnumSet.of(StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING))) { mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, charBuffer.length()); if (mappedByteBuffer != null) { logger.info("Writing to file..."); mappedByteBuffer.put(Charset.forName(charEncoding).encode(charBuffer)); logger.info("Done!"); } } catch (IOException ioe) { logger.log(Level.SEVERE, ioe.getMessage()); ioe.printStackTrace(); } } } catch (Exception e) { logger.log(Level.SEVERE, e.getMessage()); e.printStackTrace(System.err); } } }
Let’s explain the methods used in the previous code
public final ByteBuffer put(byte[] src)
– This method transfers the entire content of the given source byte array into this buffer.
The output of the command
com.javacodegeeks.examples.mappedbytebuffer.App
should be similar to:
Sep 30, 2014 1:26:08 PM com.javacodegeeks.examples.mappedbytebuffer.App main INFO: Reading file... Sep 30, 2014 1:26:08 PM com.javacodegeeks.examples.mappedbytebuffer.App main INFO: File content: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas et ligula metus. Quisque ullamcorper, nisi sit amet hendrerit iaculis, lacus nibh sodales ante, tincidunt facilisis elit ligula quis risus. Donec sed enim placerat, interdum elit ut, rhoncus erat. Vestibulum id lobortis enim. Morbi dolor metus, auctor sollicitudin diam nec, mollis venenatis nibh. Pellentesque habitant morbi tristique senectus et netus et malesuadafames ac turpis egestas. Duis commodo massa sed quam maximus blandit. Sep 30, 2014 1:26:08 PM com.javacodegeeks.examples.mappedbytebuffer.App main INFO: Writing to file... Sep 30, 2014 1:26:08 PM com.javacodegeeks.examples.mappedbytebuffer.App main INFO: Done!
3. Download the Eclipse project of this tutorial:
This was an example of how to set use the MappedByteBuffer
Class.
You can download the full source code of this example here : mappedbytebuffer.zip