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 }

时间: 2024-10-14 13:06:31

Java NIO入门的相关文章

java NIO入门【原】

server package com.server; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; public class Server { public static void main(String[] args) throws Except

Java nio 笔记:系统IO、缓冲区、流IO、socket通道

一.Java IO 和 系统 IO 不匹配 在大多数情况下,Java 应用程序并非真的受着 I/O 的束缚.操作系统并非不能快速传送数据,让 Java 有事可做:相反,是 JVM 自身在 I/O 方面效率欠佳.操作系统与 Java 基于流的 I/O模型有些不匹配.操作系统要移动的是大块数据(缓冲区),这往往是在硬件直接存储器存取(DMA)的协助下完成的.而 JVM 的 I/O 操作类喜欢操作小块数据--单个字节.几行文本.结果,操作系统送来整缓冲区的数据,java.io 包的流数据类再花大量时间

NIO入门系列之第5章:关于缓冲区的更多内容

第5章 关于缓冲区的更多内容 5.1  概述 到目前为止,您已经学习了使用缓冲区进行日常工作所需要掌握的大部分内容.我们的例子没怎么超出标准的读/写过程种类,在原来的 I/O中可以像在 NIO 中一样容易地实现这样的标准读写过程. 本节将讨论使用缓冲区的一些更复杂的方面,比如缓冲区分配.包装和分片.我们还会讨论 NIO 带给 Java 平台的一些新功能.您将学到如何创建不同类型的缓冲区以达到不同的目的,如可保护数据不被修改的只读缓冲区,和直接映射到底层操作系统缓冲区的直接缓冲区.我们将在本节的最

NIO入门系列之第6章:分散和聚集

第6章 分散和聚集 6.1  概述 分散/聚集 I/O 是使用多个而不是单个缓冲区来保存数据的读写方法. 一个分散的读取就像一个常规通道读取,只不过它是将数据读到一个缓冲区数组中而不是读到单个缓冲区中.同样地,一个聚集写入是向缓冲区数组而不是向单个缓冲区写入数据. 分散/聚集 I/O 对于将数据流划分为单独的部分很有用,这有助于实现复杂的数据格式. 6.2  分散/聚集 I/O 通道可以有选择地实现两个新的接口: ScatteringByteChannel 和 GatheringByteChan

NIO入门系列之第7章:文件锁定

第7章 文件锁定 7.1  概述 文件锁定初看起来可能让人迷惑.它似乎指的是防止程序或者用户访问特定文件.事实上,文件锁就像常规的 Java 对象锁-它们是劝告式的(advisory)锁.它们不阻止任何形式的数据访问,相反,它们通过锁的共享和获取赖允许系统的不同部分相互协调. 您可以锁定整个文件或者文件的一部分.如果您获取一个排它锁,那么其他人就不能获得同一个文件或者文件的一部分上的锁.如果您获得一个共享锁,那么其他人可以获得同一个文件或者文件一部分上的共享锁,但是不能获得排它锁.文件锁定并不总

Java NIO框架Mina、Netty、Grizzly介绍与对比(zz)

Mina: Mina(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架.当前发行的 Mina 版本2.04支持基于 Java NIO 技术的 TCP/UDP 应用程序开发.串口通讯程序,Mina 所支持的功能也在进一步的扩展中.目前,正在使用 Mina的应用包括:Apache Directory Project.AsyncWeb.AMQP(A

支撑 Java NIO 与 NodeJS 的底层技术

众所周知在近几个版本的Java中增加了一些对Java NIO.NIO2的支持,与此同时NodeJS技术栈中最为人称道的优势之一就是其高性能IO,那么我们今天要讨论的话题就是支撑这些技术的底层技术. 开始之前先要提出的一个问题是: 为什么NodeJS和Java NIO2没有在更早的时间出现? 答案:个人认为是底层的支撑技术还不成熟. 那么,底层技术指的是什么呢?对的,我想很多人已经猜到,是操作系统技术.本文提出的两个概念Java NIO2和NodeJS无一例外都是用户态技术或者说是应用层技术,而这

NIO入门系列之第4章:缓冲区内部细节

4.1  概述 本节将介绍 NIO 中两个重要的缓冲区组件:状态变量和访问方法 (accessor). 状态变量是前一节中提到的"内部统计机制"的关键.每一个读/写操作都会改变缓冲区的状态.通过记录和跟踪这些变化,缓冲区就可能够内部地管理自己的资源. 在从通道读取数据时,数据被放入到缓冲区.在有些情况下,可以将这个缓冲区直接写入另一个通道,但是在一般情况下,您还需要查看数据.这时使用访问方法 get() 来完成的.同样,如果要将原始数据放入缓冲区中,就要使用访问方法 put(). 在本

NIO入门系列之第3章:从理论到实践:NIO 中的读和写

3.1  概述 读和写是 I/O 的基本过程.从一个通道中读取很简单:只需创建一个缓冲区,然后让通道将数据读到这个缓冲区中.写入也相当简单:创建一个缓冲区,用数据填充它,然后让通道用这些数据来执行写入操作. 在本节中,我们将学习有关在Java 程序中读取和写入数据的一些知识.我们将回顾 NIO 的主要组件(缓冲区.通道和一些相关的方法),看看它们是如何交互以进行读写的.在接下来的几节中,我们将更详细地分析这其中的每个组件以及其交互. 3.2  从文件中读取 在我们第一个练习中,我们将从一个文件中