MappedByteBuffer高速缓存文件、RandomAccessFile随机访问

说到高速缓存存储,处理读写文件,那就不得不说MappedByteBuffer。

看了好多文章以后写一下自己的总结。

在这里先介绍一下相关的类与方法。

先说一下Buffer、ByteBuffer、MappedByteBuffer这几个类之间的关系。

public abstract class Buffer {

    // Invariants: mark <= position <= limit <= capacity
    private int mark = -1;
    private int position = 0;
    private int limit;
    private int capacity;
    long address;
    ......
}

public abstract class ByteBuffer extends Buffer implements Comparable {

    // These fields are declared here rather than in Heap-X-Buffer in order to
    // reduce the number of virtual method invocations needed to access these
    // values, which is especially costly when coding small buffers.
    //
    final byte[] hb;   // Non-null only for heap buffers
    final int offset;
    boolean isReadOnly;   // Valid only for heap buffers
    boolean bigEndian;
    boolean nativeByteOrder;
    ......
}
//字节数组final byte[] hb就是所指的那块内存缓冲区

public abstract class MappedByteBuffer extends ByteBuffer{
	private final FileDescriptor fd;
	......
}

public abstract class MappedByteBuffer extends ByteBuffer 直接字节缓冲区,其内容是文件的内存映射区域。

映射的字节缓冲区是通过 FileChannel.map 方法创建的。此类用特定于内存映射文件区域的操作扩展 ByteBuffer 类。

映射的字节缓冲区和它所表示的文件映射关系在该缓冲区本身成为垃圾回收缓冲区之前一直保持有效。

映射的字节缓冲区的内容可以随时更改,例如,在此程序或另一个程序更改了对应的映射文件区域的内容的情况下。这些更改是否发生(以及何时发生)与操作系统无关,因此是未指定的。

全部或部分映射的字节缓冲区可能随时成为不可访问的,例如,如果我们截取映射的文件。试图访问映射的字节缓冲区的不可访问区域将不会更改缓冲区的内容,并导致在访问时或访问后的某个时刻抛出未指定的异常。因此强烈推荐采取适当的预防措施,以避免此程序或另一个同时运行的程序对映射的文件执行操作(读写文件内容除外)。

除此之外,映射的字节缓冲区的功能与普通的直接字节缓冲区完全相同。

public RandomAccessFile(File file, String mode)throws FileNotFoundException

创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。将创建一个新的 FileDescriptor 对象来表示此文件的连接。

r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。

"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。

"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。

"rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。

public final FileChannel getChannel()返回与此文件关联的唯一 FileChannel 对象。

返回通道的 java.nio.channels.FileChannel#position()position 将始终等于 getFilePointer 方法返回的此对象的文件指针偏移量。显式或者通过读取或写入字节来更改此对象的文件指针偏移量将更改通道的位置,反之亦然。通过此对象更改此文件的长度将更改通过文件通道看到的长度,反之亦然。

public abstract MappedByteBuffer map(FileChannel.MapMode mode,long position,long size)throws IOException将此通道的文件区域直接映射到内存中。

mode - 根据是按只读、读取/写入或专用(写入时拷贝)来映射文件,分别为 FileChannel.MapMode类中所定义的 READ_ONLY、READ_WRITE 或 PRIVATE 之一

position - 文件中的位置,映射区域从此位置开始;必须为非负数

size - 要映射的区域大小;必须为非负数且不大于 Integer.MAX_VALUE

下面一个运行的例子:

package com.tzx.ne;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class MappedByteBufferDemo {
	public static void main(String[] args) throws Exception{
		/**
		 * output: 0.001s(读)
		 * input: 0.11s(写)
		 * */
		MappedByteBufferTest();
		/**
		 * size=1024*8
		 * out: 0.0s
		 * input: 0.014s
		 * */
		/**
		 * size=1024*1024*8
		 * output: 0.01s
		 * input: 0.014s
		 * */
		/**
		 * size=80
		 * output: 0.0s
		 * input: 0.546s
		 * */
		//BufferTest();
		/**
		 * time: 0.585s
		 * */
		//BufferedInputStreamTest();
	}

	/*
	 * 测试结果与Buffer size有关
	 */
	// 1、使用MappedByteBuffer: 0.7s
	public static void MappedByteBufferTest() throws Exception{
		String srcFile = "F:\\Ebook\\偷天.txt";
		String destFile = "F:\\Ebook\\toutian.txt";
		RandomAccessFile rafi = new RandomAccessFile(srcFile, "r");
		RandomAccessFile rafo = new RandomAccessFile(destFile, "rw");
		FileChannel fci = rafi.getChannel();
		FileChannel fco = rafo.getChannel();
		long size = fci.size();
		byte b;
		long start = System.currentTimeMillis();
		MappedByteBuffer mbbi = fci.map(FileChannel.MapMode.READ_ONLY, 0, size);
		System.out.println("output: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
		MappedByteBuffer mbbo = fco.map(FileChannel.MapMode.READ_WRITE, 0, size);
		start = System.currentTimeMillis();
		for (int i = 0; i < size; i++) {
			b = mbbi.get(i);
			mbbo.put(i, b);
		}
		fci.close();
		fco.close();
		rafi.close();
		rafo.close();
		System.out.println("input: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
	}

	// 2、自己处理Buffer(RandomAccessFile): 0.13s
	public static void BufferTest() throws Exception{
		String srcFile = "F:\\Ebook\\偷天.txt";
		String destFile = "F:\\Ebook\\toutian.txt";
		RandomAccessFile rafi = new RandomAccessFile(srcFile, "r");
		RandomAccessFile rafo = new RandomAccessFile(destFile, "rw");

		byte[] buf = new byte[80];
		long start = System.currentTimeMillis();
		int c = rafi.read(buf);
		System.out.println("output: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
		start = System.currentTimeMillis();
		while (c > 0) {
			if (c == buf.length) {
				rafo.write(buf);
			} else {
				rafo.write(buf, 0, c);
			}

			c = rafi.read(buf);
		}
		System.out.println("input: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");
		rafi.close();
		rafo.close();

	}

	// 3、BufferedInputStream&BufferedOutputStream: 3.02s
	public static void BufferedInputStreamTest() throws Exception{
		String srcFile = "F:\\Ebook\\偷天.txt";
		String destFile = "F:\\Ebook\\toutian.txt";
		FileInputStream rafi = new FileInputStream(srcFile);
		FileOutputStream rafo = new FileOutputStream(destFile);

		BufferedInputStream bis = new BufferedInputStream(rafi, 8192);
		BufferedOutputStream bos = new BufferedOutputStream(rafo, 8192);
		long size = rafi.available();

		long start = System.currentTimeMillis();

		for (int i = 0; i < size; i++) {
			byte b = (byte) bis.read();
			bos.write(b);
		}
		rafi.close();
		rafo.close();
		System.out.println("time: " + (double) (System.currentTimeMillis() - start) / 1000 + "s");

	}
}

总结:

1、RandomAccessFile是Java输入输出流体系中功能最丰富的文件内容访问类,他提供 了众多的方法来访问文件,它既可以读取文件的内容,也可以说向文件输出数据,本身不带缓冲读写,和FileInputStream、FileOutputStream等一样,直接按字节读写时,性能不可接受;

2、使用MappedByteBuffer读写,固然性能会得到极大提升;其实只要自己处理缓冲,性能都会有非常大的提升,比如以下两种方式中第一种使用了MappedByteBuffer,第二种自己进行缓冲处理后,对于几兆的文件,后者的效率甚至高于前者,可以从几个size大小看出运行速度,当size较大的时候一次性的读取速度是慢些,但是整体的效率非常之高。

3、BufferedXXXX之类的缓冲流,如果仅使用默认的buffer size,性能不一定最优,要权衡不同情况各种因素设置大小。

MappedByteBuffer高速缓存文件、RandomAccessFile随机访问,布布扣,bubuko.com

时间: 2024-12-28 03:47:48

MappedByteBuffer高速缓存文件、RandomAccessFile随机访问的相关文章

随机访问文件RandomAccessFile 与 内存映射文件MappedByteBuffer

一.RandomAccessFile RandomAccessFile是用来访问那些保存数据记录的文件的,你就可以用seek( )方法来访问记录,并进行读写了.这些记录的大小不必相同:但是其大小和位置必须是可知的.但是该类仅限于操作文件. RandomAccessFile不属于InputStream和OutputStream类系的.实际上,除了实现DataInput和DataOutput接口之外(DataInputStream和DataOutputStream也实现了这两个接口),它和这两个类系

Java基础知识强化之IO流笔记63:随机访问流RandomAccessFile

1. 随机访问流RandomAccessFile RandomAccessFile类不属于流,是Object类的子类.但它融合了InputStream和OutputStream的功能.支持对随机访问文件的读取和写入. RandomAccessFile的构造方法: 构造方法摘要 RandomAccessFile(File file, String mode)           创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定. RandomAccessFile(St

黑马程序员——————&gt; 随机访问流

RandomAccessFile是java输入/输出流体系中功能最丰富的问几十年内容访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据.与普通的输入/输出流不同的是,RandomAccessFile支持“随机访问”的方式.程序可以直接跳转到文件的任意地方来读写数据. 由于RandomAccessFile可以自由访问文件的任意位置,所以如果只需要访问文件部分内容,而不是把文件从头读到尾,使用RandomAccessFile将是更好的选择. 与OutputStrea

java 21 - 12 随机访问流(不属于IO流)

随机访问流: RandomAccessFile类不属于流,是Object类的子类. 但它融合了InputStream和OutputStream的功能. 支持对文件的随机访问读取和写入. public RandomAccessFile(String name,String mode):第一个参数是文件路径,第二个参数是操作文件的模 mode:参数指定用以打开文件的访问模式.允许的值及其含意为: 值 含意 "r" 以只读方式打开.调用结果对象的任何 write 方法都将导致抛出 IOExc

Java API —— IO流(数据操作流 &amp; 内存操作流 &amp; 打印流 &amp; 标准输入输出流 &amp; 随机访问流 &amp; 合并流 &amp; 序列化流 &amp; Properties &amp; NIO)

1.操作基本数据类型的流 1) 操作基本数据类型 · DataInputStream:数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型.应用程序可以使用数据输出流写入稍后由数据输入流读取的数据. · DataOutputStream:数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中.然后,应用程序可以使用数据输入流将数据读入. package datastreamdemos; import java.io.*; /** * Created b

《七》随机访问文件流

1.什么是 随机访问文件流 RandomAccessFile? 该类的实例支持读取和写入随机访问文件. 随机访问文件的行为类似于存储在文件系统中的大量字节. 有一种游标,或索引到隐含的数组,称为文件指针 ; 输入操作读取从文件指针开始的字节,并使文件指针超过读取的字节. 如果在读/写模式下创建随机访问文件,则输出操作也可用; 输出操作从文件指针开始写入字节,并将文件指针提前到写入的字节. 写入隐式数组的当前端的输出操作会导致扩展数组. 文件指针可以通过读取getFilePointer方法和由设置

Java I/O---RandomAccessFile类(随机访问文件的读取和写入)

1.JDK API中RandomAccessFile类的描述 此类的实例支持对随机访问文件的读取和写入.随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组.存在指向该隐含数组的光标或索引,称为文件指针:输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针.如果随机访问文件以读取/写入模式创建,则输出操作也可用:输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针.写入隐含数组的当前末尾之后的输出操作导致该数组扩展.该文件指针(实现数组随机读写)可以通过

随机访问

RandomAccessFile 是一个进行随机文件I/O(在字节层次上)的类.这个类提供一个seek方法,和 C/C++中的相似,移动文件指针到任意的位置,然后从那个位置字节可以被读取或写入. seek方法访问底层的运行时系统因此往往是消耗巨大的.一个更好的代替是在RandomAccessFile上建立你自己的缓冲,并实现一个直接的字节read方法.read方法的参数是字节偏移量(>= 0).这样的一个例子是: import java.io.*; public class ReadRandom

详解 随机访问流

(请观看本人博文--<详解 I/O流>) RandomAccessFile 类 (随机访问流) 概述: RandomAccessFile 类 的实例支持对随机访问文件的读取和写入. 随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组. 存在指向该隐含数组的光标或索引,称为文件指针: 输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针.如果随机访问文件以读取/写入模式创建,则输出操作也可用: 输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针.写入隐