转载

Java NIO入门

NIO 入门

前段时间在公司里处理一些大的数据,并对其进行分词、提取关键字等。虽说任务基本完成了(效果也不是特别好),对于 Java 还没入门的我来说前前后后花了 2 周的时间,我自己也是醉了。当然也有涉及到机器学习的知识,我想陆陆续续的记录下我的这一次任务的过程,也算做一个总结。

首先,手上有这么个达 G 级别的文件,按照 Java 普通 I/O 的方式肯定是不行的了,划分文件的话,也不知何年何月才能读完。所以后来上网查找了相关资料,才知道有这么个神奇的 NIO

Java 编程中, I/O 是用流的方式读取文件,所有 I/O 都被视为单个的字节的移动,通过一个称为 Stream 的对象一次移动一个字节。 Java 中新的输入 / 输出( NIO )库是在 JDK1.4 中引入的。 NIO 弥补了原来 I/O 的不足,它在标准 Java 代码中提供了高速、面向块的 I/O 。通过定义包含数据的块,以及通过以块的形式来处理这些数据, NIO 不用使用本机代码就可以利用低级优化,这是原来的 I/O 包所无法做到的。

流与块的比较

原来的 I/O 库和 NIO 最重要的区别就是数据打包和传输的方式,原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。

面向流的 I/O 系统一次一个字节的处理数据,一个输入流产生一个字节的数据,一个输出流产生一个字节的数据。

一个面向块的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按字节处理数据要快得多,即便它没有面向流的 I/O 那样的简单性。

通道和缓冲区

通道和缓冲区是 NIO 中的核心对象,几乎在每一个 I/O 操作中都要使用它们。

通道是对原 I/O 包中的流的模拟。到任何目的地或来自任何地方的所有数据都必须通过一个 Channel 对象。一个 Buffer 实质上是一个容器对象。发送给一个通道的所有对象都必须首先存放到缓冲区中;同样的,从通道中读取任何的数据都必须首先读取到缓冲区里。

什么是缓冲区?

Buffer 是一个对象, 它包含一些要写入或者刚读出的数据。 在  NIO  中加入 Buffer 对象,体现了新库与原  I/O 的一个重要区别。在面向流的   I/O 中,您将数据直接写入或者将数据直接读到 Stream 对象中。在  NIO  库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问  NIO 中的数据,您都是将它放到缓冲区中。缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程。

缓冲区类型

最常用的缓冲区类型是 ByteBuffer 。一个 ByteBuffer 可以在其底层字节数组上进行  get/set 操作(即字节的获取和设置)。 ByteBuffer 不是  NIO   中唯一的缓冲区类型。事实上,对于每一种基本 Java 类型都有一种缓冲区类型:

ByteBuffer

CharBuffer

ShortBuffer

IntBuffer

LongBuffer

FloatBuffer

DoubleBuffer

每一个 Buffer 类都是 Buffer 接口的一个实例。 除了 ByteBuffer ,每一个  Buffer  类都有完全一样的操作,只是它们所处理的数据类型不一样。因为大多数标准  I/O 操作都使用 ByteBuffer ,所以它具有所有共享的缓冲区操作以及一些特有的操作。

下面看一下 FloatBuffer 的简单例子:

1 import java.io.FileInputStream;  2 import java.io.FileNotFoundException;  3 import java.io.FileOutputStream;  4 import java.nio.ByteBuffer;  5 import java.nio.FloatBuffer;  6 import java.nio.channels.FileChannel;  7   8 // UseFloatBuffer  9  10 public class UseFloatBuffer { 11      12     public static void main(String[] args) throws Exception { 13          14         FloatBuffer fb=FloatBuffer.allocate(10); 15         for (int i=0; i<fb.capacity(); i++) { 16             float f=(float)((float)i/10*(2*Math.PI)); 17             fb.put(f); 18         } 19         fb.flip(); 20         while (fb.hasRemaining()){ 21             float f=fb.get(); 22             System.out.println(f); 23         } 24     } 25 }

什么是通道?

Channel 是一个对象,可以通过它读取和写入数据。拿  NIO  与原来的  I/O 做个比较,通道就像是流。正如前面提到的,所有数据都通过  Buffer  对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。简而言之,就是 NIO 的大致流程为:输入文件->缓冲区->通道->缓冲区->程序处理数据->缓冲区->通道->缓冲区->输出文件; I/O 的大致流程为:输入文件->流->程序处理数据->流->输出文件。

通道类型

通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类),而通道可以用于读、写或者同时用于读写。

实践起来: NIO  中的读和写

读和写是 I/O 的基本过程。从一个通道中读取很简单:只需创建一个缓冲区,然后让通道将数据读到这个缓冲区中;写入也相当简单:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入操作。

从文件中读取

如果使用原来的  I/O ,那么我们只需创建一个 FileInputStream 并从它那里读取。而在  NIO  中,情况稍有不同:我们首先从 FileInputStream 获取一个 Channel 对象,然后使用这个通道来读取数据。

在  NIO  系统中,任何时候执行一个读操作,您都是从通道中读取,但是您不是直接从通道读取。因为所有数据最终都驻留在缓冲区中,所以您是通过通道读到缓冲区中的数据。

因此读取文件涉及三个步骤:

(1) 从 FileInputStream 获取 Channel

(2) 创建 Buffer

(3) 将数据从 Channel 读到 Buffer 中。

1 FileInputStream fin=new FileInputStream("read.txt"); 2 FileChannel fc=fin.getChannel(); 3 ByteBuffer buffer=ByteBuffer.allocate(1024); 4 fc.read(buffer);

写入文件

在  NIO  中写入文件类似于从文件中读取。首先从 FileOutputStream 获取一个通道;下一步是创建一个缓冲区并在其中放入一些数据 - 在这里,数据将从一个名为 data 的数组中取出,最后一步是写入缓冲区中。

1 FileOutputStream fout=new FileOutputStream("write.txt"); 2 FileChannel fc=fout.getChannel(); 3 ByteBuffer buffer=ByteBuffer.allocate(1024); 4 for (int i=0; i<data.length; i++) { 5       buffer.put(data[i]); 6 } 7 buffer.flip(); 8 fc.write(buffer);

实战练习

我们以一个名为 CopyFile.java 的简单程序作为这个练习的基础,它将一个文件的所有内容拷贝到另一个文件中。CopyFile.java 执行三个基本操作:首先创建一个Buffer,然后从源文件中将数据读到这个缓冲区中,然后将缓冲区写入目标文件。这个程序不断重复 ― 读、写、读、写 ― 直到源文件结束。

1 // CopyFile  2   3 import java.io.*;  4 import java.nio.*;  5 import java.nio.channels.*;  6   7 public class CopyFile {  8       9     static public void main( String args[] ) throws Exception { 10         String infile="E://北京欢迎你.txt"; 11         String outfile="E://out.txt"; 12  13         FileInputStream fin=new FileInputStream(infile); 14         FileOutputStream fout=new FileOutputStream(outfile); 15         FileChannel fcin = fin.getChannel(); 16         FileChannel fcout = fout.getChannel(); 17  18         ByteBuffer buffer = ByteBuffer.allocate(1024); 19  20         while (true) { 21             buffer.clear(); 22             int r=fcin.read(buffer); 23             if (r == -1) { 24                 break; 25             } 26             buffer.flip(); 27             fcout.write(buffer); 28         } 29     } 30 }

Java NIO入门

Java NIO入门

正文到此结束
Loading...