NIO之Channel聚集(gather)写入与分散(scatter)读取

时间:2022-12-31 08:25:49
  • Channel聚集(gather)写入:

聚集写入( Gathering Writes)是指将多个 Buffer 中的数据“聚集”到 Channel。 特别注意:按照缓冲区的顺序,写入 position 和 limit 之间的数据到 Channel 。 

NIO之Channel聚集(gather)写入与分散(scatter)读取

  • Channel分散(scatter)读取:

分散读取( Scattering Reads)是指从 Channel 中读取的数据“分散” 到多个 Buffer 中。 特别注意:按照缓冲区的顺序,从 Channel 中读取的数据依次将 Buffer 填满。

NIO之Channel聚集(gather)写入与分散(scatter)读取

  • 聚集写入( Gathering Writes)和分散读取( Scattering Reads)代码示例:
      1 package com.expgiga.NIO;
      2 
      3 import java.io.FileInputStream;
      4 import java.io.FileOutputStream;
      5 import java.io.IOException;
      6 import java.io.RandomAccessFile;
      7 import java.nio.ByteBuffer;
      8 import java.nio.CharBuffer;
      9 import java.nio.MappedByteBuffer;
     10 import java.nio.channels.FileChannel;
     11 import java.nio.charset.Charset;
     12 import java.nio.charset.CharsetDecoder;
     13 import java.nio.charset.CharsetEncoder;
     14 import java.nio.file.Paths;
     15 import java.nio.file.StandardOpenOption;
     16 import java.util.Map;
     17 import java.util.Set;
     18 
     19 /**
     20  * 一、Channel:用于源节点与目标节点之间的连接。在Java NIO中,负责缓冲区中数据传输,Channel本身不存储数据,因此需要配合缓冲区进行传输。
     21  *
     22  * 二、Channel的实现类:
     23  *     java.nio.channels.Channel 接口:
     24  *     |-- FileChannel
     25  *     |-- SocketChannel
     26  *     |-- ServerSocketChannel
     27  *     |-- DatagramChannel
     28  *
     29  * 三、获取通道Channel
     30  * 1.Java针对支持通道的类提供了getChannel()方法
     31  *   本地IO
     32  *   FileInputStream/FileOutputStream
     33  *   RandomAccessFile
     34  *
     35  *   网络IO:
     36  *   Socket
     37  *   ServerSocket
     38  *   DatagramSocket
     39  *
     40  * 2.在jdk1.7中的NIO.2针对各个通道提供了静态方法open()
     41  *
     42  * 3.在jdk1.7中的NIO.2的Files工具类的newByteChannel()
     43  *
     44  * 四、通道之间的数据传输
     45  * transferFrom()
     46  * transferTo()
     47  *
     48  * 五、分散(scatter)与聚集(gather)
     49  * 分散读取(scattering Reads):将通道中的数据分散到多个缓冲区中
     50  * 聚集写入(gathering Writes):将多个缓冲区的数据聚集到通道中
     51  *
     52  * 六、字符集Charset
     53  * 编码:字符串->字节数组
     54  * 解码:字节数组 -> 字符串
     55  *
     56  */
     57 public class TestChannel {
     58 
     59     public static void main(String[] args) throws IOException {
     60 
     61         /*
     62          * 1.利用通道完成文件的复制(非直接缓冲区)
     63          */
     64         FileInputStream fis = null;
     65         FileOutputStream fos = null;
     66 
     67         FileChannel inChannel = null;
     68         FileChannel outChannel = null;
     69 
     70         try {
     71             fis = new FileInputStream("1.jpg");
     72             fos = new FileOutputStream("2.jpg");
     73             //1.获取通道
     74             inChannel = fis.getChannel();
     75             outChannel = fos.getChannel();
     76 
     77             //2.分配指定大小的缓冲区
     78             ByteBuffer buffer = ByteBuffer.allocate(1024);
     79 
     80             //3.将通道中的数据缓冲区中
     81             while (inChannel.read(buffer) != -1) {
     82 
     83                 buffer.flip();//切换成都数据模式
     84 
     85                 //4.将缓冲区中的数据写入通道中
     86                 outChannel.write(buffer);
     87                 buffer.clear();//清空缓冲区
     88             }
     89         } catch (Exception e) {
     90             e.printStackTrace();
     91         } finally {
     92             if (outChannel != null) {
     93                 try {
     94                     outChannel.close();
     95                 } catch (IOException e) {
     96                     e.printStackTrace();
     97                 }
     98             }
     99 
    100             if (inChannel != null) {
    101                 try {
    102                     inChannel.close();
    103                 } catch (IOException e) {
    104                     e.printStackTrace();
    105                 }
    106             }
    107 
    108             if (fis != null) {
    109                 try {
    110                     fis.close();
    111                 } catch (IOException e) {
    112                     e.printStackTrace();
    113                 }
    114             }
    115 
    116             if (fos != null) {
    117                 try {
    118                     fos.close();
    119                 } catch (IOException e) {
    120                     e.printStackTrace();
    121                 }
    122             }
    123         }
    124 
    125 
    126         /*
    127          * 2.利用(直接缓冲区)通道完成文件的复制(内存映射文件的方式)
    128          */
    129 
    130         long start = System.currentTimeMillis();
    131         FileChannel inChannel2 = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
    132         FileChannel outChannel2 = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
    133 
    134         //内存映射文件
    135         MappedByteBuffer inMappedBuf = inChannel2.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
    136         MappedByteBuffer outMappedBuf = outChannel2.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
    137 
    138         //直接对缓冲区进行数据读写操作
    139         byte[] dst = new byte[inMappedBuf.limit()];
    140         inMappedBuf.get(dst);
    141         outMappedBuf.put(dst);
    142 
    143         inChannel2.close();
    144         outChannel2.close();
    145 
    146         long end = System.currentTimeMillis();
    147         System.out.println("耗费的时间为:" + (end - start));
    148 
    149         /*
    150          * 通道之间的数据传输(直接缓冲区)
    151          */
    152         FileChannel inChannel3 = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
    153         FileChannel outChannel3 = FileChannel.open(Paths.get("3.jpg"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE);
    154 
    155         inChannel3.transferTo(0, inChannel3.size(), outChannel3);
    156         //等价于
    157 //        outChannel3.transferFrom(inChannel3, 0, inChannel3.size());
    158 
    159         inChannel3.close();
    160         outChannel3.close();
    161 
    162         /*
    163          * 分散和聚集
    164          */
    165         RandomAccessFile randomAccessFile1 = new RandomAccessFile("1.txt", "rw");
    166 
    167         //1.获取通道
    168         FileChannel fileChannel1 = randomAccessFile1.getChannel();
    169 
    170         //2.分配指定大小的缓冲区
    171         ByteBuffer buf1 = ByteBuffer.allocate(100);
    172         ByteBuffer buf2 = ByteBuffer.allocate(2014);
    173 
    174         //3.分散读取
    175         ByteBuffer[] bufs = {buf1, buf2};
    176         fileChannel1.read(bufs);
    177 
    178         for (ByteBuffer byteBuffer : bufs) {
    179             byteBuffer.flip();
    180         }
    181         System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
    182         System.out.println("----------------------------");
    183         System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));
    184 
    185         //4.聚集写入
    186         RandomAccessFile randomAccessFile2 = new RandomAccessFile("2.txt", "rw");
    187         FileChannel fileChannel2 = randomAccessFile2.getChannel();
    188         fileChannel2.write(bufs);
    189 
    190 
    191         /*
    192          * 字符集
    193          */
    194         Map<String, Charset> map = Charset.availableCharsets();
    195 
    196         Set<Map.Entry<String, Charset>> set = map.entrySet();
    197 
    198         for (Map.Entry<String, Charset> entry : set) {
    199             System.out.println(entry.getKey() + "=" + entry.getValue());
    200         }
    201 
    202 
    203 
    204         Charset cs1 = Charset.forName("GBK");
    205         //获取编码器和解码器
    206         CharsetEncoder ce = cs1.newEncoder();
    207 
    208         //获取解码器
    209         CharsetDecoder cd = cs1.newDecoder();
    210 
    211         CharBuffer cBuf = CharBuffer.allocate(1024);
    212         cBuf.put("hello world!");
    213         cBuf.flip();
    214 
    215         //编码
    216         ByteBuffer bBuf = ce.encode(cBuf);
    217 
    218         for (int i = 0; i < 12; i++) {
    219             System.out.println(bBuf.get());
    220         }
    221 
    222         //解码
    223         bBuf.flip();
    224         CharBuffer cBuf2 = cd.decode(bBuf);
    225         System.out.println(cBuf2.toString());
    226 
    227         System.out.println("----------------------------");
    228         Charset cs2 = Charset.forName("UTF-8"); //"GBK"
    229         bBuf.flip();
    230         CharBuffer cBuf3 = cs2.decode(bBuf);
    231         System.out.println(cBuf3);
    232     }
    233 }