NIO按行读写数据

[b] Nio学习[/b]
[i]——如何以行为单位来读写数据 Victor[/i]
最近在学习Nio,想比较出Nio与io之间的效率。确实发现Nio在大部分情况下比io要快、消耗的内存与CPU要小,在处理大数据、多并发的情况下,使用Nio更好。
随着学习的深入,却发现Nio没有按行读取文件的方法。这在某种特殊要求下,无疑限制了Nio的使用,于是我试着自己实现下Nio按行读写的功能。
首先,实现读取一行的功能,具体代码如下:
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
* Use for reader line from file by use Nio
*
* @author Victor
*/
public class NioReader {
private static final String lineSeparator = "\r\n";//换行关键字
private final int size = 1024 * 8;//块的大小
private static int n;//该文件可分成块数量
private FileInputStream fin;//读取文件的流
private FileChannel fcin;//管道
private static int count = 0;//记录该块读取到第几行
private static int piece = 0;//记录读取到哪一块
private static String[] strs;//该块转换后的数组,一行数据对应一个元素

public NioReader(String path)
throws IOException {
fin = new FileInputStream(path);
fcin = fin.getChannel();
long length = fcin.size();
n = (int)length / size;
strs = getStrsBySize();
}

public String getNextLine()
throws IOException {
String s = null;
int lengh = strs.length - 1;
if (count < lengh) {
s = strs[count];
count++;
} else if (count == lengh) {
if (piece > n) {
s = strs[count];
count++;
} else {
strs = getStrsBySize();
s = getNextLine();
}
}
return s;
}

/**
* 获取下一块的String数组
*/
private String[] getStrsBySize()
throws IOException {
count = 0;
if (piece <= n) {
long opint = piece * size;
MappedByteBuffer buffer;
byte[] content = new byte[size];
if (piece == n) {
long length = fcin.size();
int finalsize = (int)(length - opint);
buffer = fcin.map(FileChannel.MapMode.READ_ONLY, opint,
finalsize);
buffer.get(content, 0, finalsize);
} else {
buffer = fcin.map(FileChannel.MapMode.READ_ONLY, opint,
size);
buffer.get(content, 0, size);
}
String str;
if (piece == 0) {
str = new String(content);
str.trim();
} else {
String newStr = new String(content);
newStr.trim();
str = strs[strs.length - 1] + newStr;
}
strs = str.split(lineSeparator);
piece++;
}
return strs;
}

public void close()
throws IOException {
if (fin != null) {
fin.close();
}
if (fcin != null) {
fcin.close();
}
}
}

然后实现写一行的功能:
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NioWriter {
private static final String lineSeparator = "\r\n";
private FileOutputStream fout;
private FileChannel fcout;
private ByteBuffer buffer;

public NioWriter(String path) throws IOException {
fout = new FileOutputStream(path);
fcout = fout.getChannel();
}

public void putln(String s) {
if (fcout == null) {
throw new IllegalArgumentException(
"attempt to use a closed Writer");
}
buffer= ByteBuffer.allocate(1024*8);
buffer.clear();
s = s.trim() + lineSeparator;
byte[] bytes = s.getBytes();
buffer.put(bytes);
buffer.flip();
try {
fcout.write(buffer);
fout.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public void close() throws IOException {
if (fout != null) {
fout.close();
}
if (fcout != null) {
fcout.close();
}
}
}

接下来就是测试了,为了方便测试它的内存消耗,我还特意写个测试内存的工具类:
public class RuntimeMemory {
private static final int MB = 1024 * 1024;
public static DecimalFormat df = new DecimalFormat("#####.##");

public static void getMemory() {
Runtime run = Runtime.getRuntime();
long max = run.maxMemory();
long total = run.totalMemory();
long free = run.freeMemory();
long usable = max - total + free;
StringBuffer sb = new StringBuffer();
sb.append("[RuntimeMemory] Max :").append(getMb(max)).append(
" Assigned :").append(getMb(total)).append(" Free :").append(
getMb(free)).append(" Usable :").append(getMb(usable));
System.out.println(sb.toString());
}
private static String getMb(long memory) {
double mb = (double)memory / MB;
return "[" + df.format(mb) + " MB]; ";
}
}

主测试类:
public class TestNio {

/**
* @author Victor
* @throws IOException
*/
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
RuntimeMemory.getMemory();
String infile = "D:\\test.txt";
String outfile = "D:\\copy.txt";

NioReader reader=new NioReader(infile);
NioWriter writer=new NioWriter(outfile);
String line;

while ((line = reader.getNextLine()) != null) {
try {
if ((line == null) || line.trim().equals("")) {
continue;
}
writer.putln(line);
} catch (Exception ex) {
ex.printStackTrace();
}
}
reader.close();
writer.close();
long end = System.currentTimeMillis();
System.out.println((end - start) + "ms");
RuntimeMemory.getMemory();
}
}

当然为了对比,我另外还写了普通io的测试,在此就不展示代码了,下面是我对二者测试的结果分析:
1. 当数据量为7M时:
Nio:
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.4 MB]; Usable :[63.02 MB];
1201ms 1357ms 1279ms 1295ms 1404ms (运行了多次)
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.5 MB]; Usable :[63.13 MB];
Io:
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.39 MB]; Usable :[63.02 MB];
281ms 234ms 249ms 265ms 234ms
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[3.77 MB]; Usable :[62.4 MB];
2. 当数据量为370M时:
Nio:
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.4 MB]; Usable :[63.02 MB];
55799ms
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[3.31 MB]; Usable :[61.94 MB];
Io:
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.39 MB]; Usable :[63.02 MB];
9882ms
[RuntimeMemory] Max :[63.56 MB]; Assigned :[4.94 MB]; Free :[4.39 MB]; Usable :[63.02 MB];
一些小数据也测试过,但差别不大,就不列举了。
此外,对于CPU的消耗,在运行时,我进行了监测,相对来说使用Nio所消耗的CPU较大些。

[b]从上面的数据可以分析出:[/b]
用Nio来以行为单位读写数据的速度是普通Io的5-6倍(汗!!!可能我写的代码并不完善),虽然Nio在内存的消耗上占据一点优势,但不明显,消耗的CPU却更多。
宗上,在需要以行为单位来读写数据时,还是得采用普通IO。。。。

[b]再说说测试中所遇到的问题:[/b]
首先,就是reader的时候,因为它是按指定的大小拆分块,有可能会把一行拆分到不同的块中,所以我采取的策略就是保留每块转换成数组后的最后一个元素,添加到下一个块中。
然后,就是在写数据的时候,要注意ByteBuffer的大小,由于我没有txt的大数据,采用了一个数据库的脚本,结果运行时老是报缓冲区溢出的异常,找了很久。。。才发现是被复制的文本有问题:那个sql中有些行竟然有将近1M的大小,哎!
最后,再说一些还没有解决的问题,就是在更大的数据拷贝完成后,悲催的发现,拷贝后的文件竟然比源文件要大一些(源文件500M的样子,复制的文件多出了5-6k,由于数据大多了,无法仔细分析)!
(新手!仅供参考!求前辈指点!)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值