1.7 NIO
JDK 1.4 开始,引入了 java.nio 包
主要功能:
字符集编码解码
非阻塞IO
内存映射文件
文件加锁机制
1.7.1 内存映射文件
将虚拟内存映射,从而实现文件的方法和操作,这样处理,要比传统IO要快。可以看做传统IO是拷贝完副本后进行一系列操作,再照着修改原数据;内存映射是直接改原数据,省略了拷贝过程。
获取通道
FileInputStream fio = new FileInputStream("");
FileChannel channel = in.getChannel();
获取映射
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY,0,length);
这里的模式有:
顺序遍历
while(buffer.hasRetaining()){
byte b = buffer.get();
}
随机访问
Byte b = buffer.get(i);
判断文件损坏,计算校验和(不算文件会损坏)
for(int p = 0; p < length; p++){
int c = buffer.get(p);
crc.update(c);
}
return crc.getValue();
作者示例,比较不同方式读取数据计算校验和的效率:
❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤
import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.util.zip.CRC32;
public class NIOTest {
public static long checksumInputStream(String filename) throws IOException {
InputStream in = new FileInputStream(filename);
CRC32 crc = new CRC32();
int c;
while((c=in.read())!=-1){
crc.update(c);
}
return crc.getValue();
}
public static long checksumBufferedInputStream(String filename) throws IOException{
InputStream in = new BufferedInputStream(new FileInputStream(filename));
CRC32 crc = new CRC32();
int c;
while((c=in.read())!=-1){
crc.update(c);
}
return crc.getValue();
}
public static long checksumRandomAccessFile(String filename) throws IOException{
RandomAccessFile file = new RandomAccessFile(filename,"r");
long length = file.length();
CRC32 crc = new CRC32();
for(long p = 0; p < length; p++){
file.seek(p);
int c = file.readByte();
crc.update(c);
}
return crc.getValue();
}
public static long checksumMappedFile(String filename) throws IOException{
FileInputStream in = new FileInputStream(filename);
FileChannel channel = in.getChannel();
CRC32 crc = new CRC32();
int length = (int) channel.size();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
for(int p = 0; p < length; p++){
int c = buffer.get(p);
crc.update(c);
}
return crc.getValue();
}
public static void main(String[] args) throws IOException {
String[] strs = new String[]{
"Input Stream:","Buffered Input Stream:","Random Access File:","Mapped File"
};
for(int i = 0; i < 4; i++){
System.out.println(strs[i]);
long start = System.currentTimeMillis();
long crcValue = getValue(i,"ext/A.txt");
long end = System.currentTimeMillis();
System.out.println(Long.toHexString(crcValue));
System.out.println((end-start)+ " milliseconds");
System.out.println();
}
}
private static long getValue(int index,String name) throws IOException {
switch (index){
case 0: return checksumInputStream(name);
case 1: return checksumBufferedInputStream(name);
case 2: return checksumRandomAccessFile(name);
case 3: return checksumMappedFile(name);
}
return 0L;
}
}
❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤
运行结果:
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
我测的结果是缓冲会快一点,可能和电脑设备之类有关,作者测试结果是内存映射快一点
1.7.2 缓冲区数据结构
进行内存映射时,可以使用缓冲区一次性读取合适的长度。
缓冲区数据结构(无StringBuffer!):
ByteBuffer
CharBuffer
DoubleBuffer
IntBuffer
LongBuffer
ShortBuffer
缓冲区特性:
容量:代表缓冲区能一次性装载数据的大小,它是固定不变的
读写位置:代表下一个读入点
界限:代表有效数据的边界,超过界限读取没有实际意义
标记:对一读写范围,可以打标记,用于重复读写
Buffer 相关方法【一般不贴,这个有点复杂】:
❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤
import java.io.*;
import java.nio.CharBuffer;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) throws IOException{
//写
String inputStr = "Love code, love java, love everything you did.";
List<CharBuffer> list = new ArrayList<>();
int n = inputStr.length();
CharBuffer buffer = CharBuffer.allocate(8);//容量
for(int i = 0; i < n; i++){
buffer.append(inputStr.charAt(i));
if(buffer.remaining()==0 || i == n-1){
list.add(buffer);
buffer = CharBuffer.allocate(8);
}
}
//读
StringBuilder sb = new StringBuilder();
int m = list.size();
for(CharBuffer b:list){
b.flip();//准备读,注意用flip设置界限,rewind的话,后面无法用remaining()判断
while (b.remaining()>0)
sb.append(b.get());
}
System.out.println(sb.toString());
}
}
❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤❤🧡💛💚💙💜🤎🖤
运行结果:
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍
🚗🚓🚕🛺🚙🚌🚐🚎🚑🚒🚚🚛🚜🚘🚔🚖🚍🦽🦼🛹🚲🛴🛵🏍