`

RandomAccessFile 类读写测试及其性能优化(一)

阅读更多
package io;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * RandomAccessFile 类读写测试及其性能优化(一)
 * 
 * GenerateIntArray生成count个数组,每个数组里面有size个整数,然后将生成的数据写入文件.
 * 整数通过Random.nextInt(total)来生成.
 * 初始化时,数组数据都是0,调用refreshDataArr()可生成新数据填充数组
 * 
 * 生成数据的方法有:
 * (1) refreshDataArr() 使用双重循环生成数据.
 * (2) refreshDataArr_M() 使用count个线程,每个线程中生成size个数据
 * 
 * 将数据写入文件的方法有:
 * (1)writeData2File(File f) 
 *    循环调用RandomAccessFile.writeInt(int)方法,每个数据都写一次,一共写了count*size次
 * (2)writeData2File_B(File f) 
 *    该方法先将coutn个整数转换成字符数组,并将count个字节数组按顺序组合到一个大的字节数组中
 *    然后调用RandomAccessFile.write(byte[] byteArr);方法一次性写入size个整数.
 * (3)writeData2File_M(File f)
 *    该方法启动count个线程,每个线程使用writeData2File_B中的方法,一次性写入size个整数
 * 
 * 由下面的测试结果可知,通过RandomAccessFile.write(byte[] byteArr)写入字节数组的方式一次性写入
 * size个整数时写入速度最快,比一次写入一个整数快了很多.多线程写入时性能提升不大,只有在count不大,但是
 * size巨大时多线程方法写入有一些提升,因为生成count个线程并且要进行线程调度也需要消耗一些系统资源.
 * 
 * 多线程方式生成数据,也只有在size特别大(100000),count不是很大时有速度提升.
 * 
 * RandomAccessFile进行多线程写似乎并不能提升速度,其中原因有待研究.
 * 
 * 数据量非常小时,使用单线程一次生成一个数据,以及一次写入一个整数时速度快
 * (因为使用写字节数组方式一次性写入size个整数时需要将整数转换成字节数组,这有一定的开销).
 * 
 * 下面是部分测试数据(耗时单位是 耗时(纳秒)/100000)
 * 
 * 
count = 10, size = 10 

正在生成数据,请稍后...
refreshDataArr 生成数据成功, 耗时:0

正在生成数据,请稍后...
refreshDataArr_M 生成数据成功, 耗时:96

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File_B写入数据耗时:16

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File 写入数据耗时:12

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File_M写入数据耗时:39

----------------------------------------------------------------
count = 100, size = 1000 

正在生成数据,请稍后...
refreshDataArr 生成数据成功, 耗时:123

正在生成数据,请稍后...
refreshDataArr_M 生成数据成功, 耗时:303

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File_B写入数据耗时:246

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File 写入数据耗时:6623

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File_M写入数据耗时:308

----------------------------------------------
count = 100, size = 10000 

正在生成数据,请稍后...
refreshDataArr 生成数据成功, 耗时:894

正在生成数据,请稍后...
refreshDataArr_M 生成数据成功, 耗时:1645

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File_B写入数据耗时:753

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File 写入数据耗时:70863

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File_M写入数据耗时:1605
------------------------------------
count = 1000, size = 10000 

正在生成数据,请稍后...
refreshDataArr 生成数据成功, 耗时:9078

正在生成数据,请稍后...
refreshDataArr_M 生成数据成功, 耗时:15067

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File_B写入数据耗时:25753

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File 写入数据耗时:701698

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File_M写入数据耗时:13727
----------------------------------------------
count = 1000, size = 100000 

正在生成数据,请稍后...
refreshDataArr 生成数据成功, 耗时:86459

正在生成数据,请稍后...
refreshDataArr_M 生成数据成功, 耗时:82820

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File_B写入数据耗时:345980

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File 写入数据耗时:0(耗时太长,这里放弃测试)

正在写入数据,请稍后...
数据已写入文件D:\D\test_data.dat\test_data.dat
writeData2File_M写入数据耗时:273550
 *
 */
public class GenerateIntArray
{
  private int     count   = 1000;             // 数组的个数,
  private int     size    = 10;               // 每个数组的元素个数
  private int[][] dataArr;
  private Random  random  = new Random(1000);

  public GenerateIntArray()
  {
    dataArr = new int[count][size];
  }

  public GenerateIntArray(int count, int size)
  {
    this.count = count;
    this.size = size;
    this.dataArr = new int[count][size];
  }

  public int[][] getDataArr()
  {
    return dataArr;
  }

  /**
   * 刷新数组中的数据
   */
  public int[][] refreshDataArr()
  {
    int total = count * size;

    for (int i = 0; i < count; i++)
    {
      for (int j = 0; j < size; j++)
      {
        dataArr[i][j] = random.nextInt(total);
      }
    }

    return dataArr;
  }
  
  private class getIntTask implements Runnable
  {
    private int arrIndex;
    private CountDownLatch latch;
    
    public getIntTask(int arrIndex,CountDownLatch latch)
    {
      this.arrIndex = arrIndex;
      this.latch = latch;
    }
    
    @Override
    public void run()
    {
      int total = count * size;
      for(int i = 0;i < size;i++)
      {
        dataArr[arrIndex][i] = random.nextInt(total);
      }
      latch.countDown();
      
    }
    
  }
  
  /*
   * 多线程方式生成数组中的数据
   * 启动count个线程,每个线程中产生size个整数
   */
  public int[][] refreshDataArr_M()
  {
    CountDownLatch latch = new CountDownLatch(count);
    ExecutorService exec = Executors.newCachedThreadPool();
    for(int i=0;i<count;i++)
    {
      exec.execute(new getIntTask(i,latch));
    }
    try
    {
      latch.await();
      //保证在所有生成数据线程没有完成之前,当前方法不返回.
    }
    catch (InterruptedException e)
    {
      e.printStackTrace();
    }
    exec.shutdown();
    
    return dataArr;
    
    
  }

  /**
   * 写数组数据到文件,如果文件已经存在,则会被删除,然后重新生成文件
   * 每次写入数组中的一个数据
   * @param f
   * @throws IOException
   */
  public void writeData2File(File f) throws IOException
  {
    if (null != f && f.exists())
    {
      f.delete();
    }
    RandomAccessFile rf = new RandomAccessFile(f, "rw");
    rf.seek(0);// 每次都从头开始些文件
    for (int i = 0; i < count; i++)
    {
      for (int j = 0; j < size; j++)
      {
        rf.writeInt(dataArr[i][j]);
      }
    }
    rf.close();
  }

  /**
   * 写数据时,现将整数转换成字节数据保存,然后一次性写入字节数组到文件,
   * 避免频繁写入.
   * @param f
   * @throws IOException
   */
  public void writeData2File_B(File f) throws IOException
  {
    if (null != f && f.exists())
    {
      f.delete();
    }
    RandomAccessFile rf = new RandomAccessFile(f, "rw");
    rf.seek(0);// 每次都从头开始些文件
    for (int i = 0; i < count; i++)
    {
      byte[] byteArr = new byte[4 * size];
      int iTmp = 0;
      for (int j = 0; j < size; j++)
      {
        byte[] tmpBytes = int2byte(dataArr[i][j]);
        byteArr[iTmp++] = tmpBytes[3];
        byteArr[iTmp++] = tmpBytes[2];
        byteArr[iTmp++] = tmpBytes[1];
        byteArr[iTmp++] = tmpBytes[0];
      }
      rf.write(byteArr);
    }
    rf.close();
  }
  
  /**
   * 多线程方式同时同时写文件
   * @param f
   * @throws IOException
   */
  
  class WriteTask implements Runnable
  {

    private File f;
    private int dataIndex;
    
    public WriteTask(File f,int dataIndex)
    {
      this.f = f;
      this.dataIndex = dataIndex;
    }
    
    @Override
    public void run()
    {
      try
      {
        RandomAccessFile rf = new RandomAccessFile(f, "rw");
        rf.skipBytes(dataIndex * size * 4 );
        byte[] byteArr = new byte[4 * size];
        int iTmp = 0;
        for (int j = 0; j < size; j++)
        {
          byte[] tmpBytes = int2byte(dataArr[dataIndex][j]);
          byteArr[iTmp++] = tmpBytes[3];
          byteArr[iTmp++] = tmpBytes[2];
          byteArr[iTmp++] = tmpBytes[1];
          byteArr[iTmp++] = tmpBytes[0];
        }
        rf.write(byteArr);
        rf.close();
      }
      catch (Exception e)
      {
        e.printStackTrace();
      }
    }
  }
  
  public void writeData2File_M(File f) throws IOException
  {
    if (null != f && f.exists())
    {
      f.delete();
    }
    
    //先生成一个固定尺寸的文件,能够保存所有整数的
    RandomAccessFile rf = new RandomAccessFile(f, "rw");
    rf.setLength(count * size * 4 ); //设置尺寸(一个整型占4字节)
    rf.seek(0);
    //rf.write(1024);//随便写一个,以便保存文件
    rf.close();
    
    ExecutorService exec = Executors.newCachedThreadPool();
    for(int i=0;i<count;i++)
    {
      exec.execute(new WriteTask(f,i));
    }
    exec.shutdown();
  }

  // 将二进制数转换成字节数组
  private byte[] int2byte(int res)
  {
    byte[] targets = new byte[4];

    targets[0] = (byte) (res & 0xff);// 最低位
    targets[1] = (byte) ((res >> 8) & 0xff);// 次低位
    targets[2] = (byte) ((res >> 16) & 0xff);// 次高位
    targets[3] = (byte) (res >>> 24);// 最高位,无符号右移
    return targets;
  }

  public static void main(String[] args)
  {
    int count = 10;
    int size = 1000;
    boolean bPrintData = false; //是否打印生成的数组,当数据量大是不打印,只在小数据量时打印以便测试
    
    System.out.printf("count = %d, size = %d \n\n",count,size);
    
    GenerateIntArray generator = new GenerateIntArray(count, size);

    File f;

    try
    {
      f = new File("D:\\D\\test_data.dat");
      
      System.out.println("正在生成数据,请稍后...");
      long startTmie = System.nanoTime();
      generator.refreshDataArr();
      long totalTime = (System.nanoTime() - startTmie)/ 100000;
      System.out.println("refreshDataArr 生成数据成功, 耗时:" + totalTime);
      System.out.println();
      
      System.out.println("正在生成数据,请稍后...");
      startTmie = System.nanoTime();
      generator.refreshDataArr_M();
      totalTime = (System.nanoTime() - startTmie)/ 100000;;
      System.out.println("refreshDataArr_M 生成数据成功, 耗时:" + totalTime);
      System.out.println();
      
      System.out.println("正在写入数据,请稍后...");
      startTmie = System.nanoTime();
      generator.writeData2File_B(f);
      totalTime = (System.nanoTime() - startTmie)/ 100000;;
      System.out.println("数据已写入文件" + f.getPath() + File.separator + f.getName());
      System.out.println("writeData2File_B写入数据耗时:" + totalTime);
      System.out.println();
      
      System.out.println("正在写入数据,请稍后...");
      startTmie = System.nanoTime();
      generator.writeData2File(f);
      totalTime = (System.nanoTime() - startTmie)/ 100000;
      System.out.println("数据已写入文件" + f.getPath() + File.separator + f.getName());
      System.out.println("writeData2File 写入数据耗时:" + totalTime);
      System.out.println();
      
      System.out.println("正在写入数据,请稍后...");
      startTmie = System.nanoTime();
      generator.writeData2File_M(f);
      totalTime = (System.nanoTime() - startTmie)/ 100000;;
      System.out.println("数据已写入文件" + f.getPath() + File.separator + f.getName());
      System.out.println("writeData2File_M写入数据耗时:" + totalTime);
      System.out.println();
      
      
      if(bPrintData)
      {
        System.out.println("原始数组中生成的数据...");
        int[][] intArr = generator.getDataArr();
        for (int i = 0; i < count; i++)
        {
          for (int j = 0; j < size; j++)
          {
            System.out.printf("%d ", intArr[i][j]);
          }
          System.out.println();
        }
        
        System.out.println("从文件中读取出来的数据...");
        RandomAccessFile rf = new RandomAccessFile(f, "r");
        rf.seek(0);
        int iline = 1;
        while (true)
        {
          System.out.printf("%d ",rf.readInt());
          if(iline % size == 0)
          {
            System.out.println();
          }
          iline ++;
          // 判断已经到文件尾了
          if (rf.getFilePointer() >= rf.length() - 1)
          {
            break;
          }
          
        }
        rf.close();
      }

      
    }
    catch (Exception e)
    {
      e.printStackTrace();
    }

  }

}

 

分享到:
评论

相关推荐

    java的RandomAccessFile对文件进行读写操作

    Java中的`RandomAccessFile`类是用于处理文件的高级接口,它允许我们对文件进行随机访问,即在文件的任何位置进行读写操作,而不仅仅是从文件头开始顺序读写。这种能力使得`RandomAccessFile`在处理大文件或者需要...

    RandomAccessFile类的用法.doc

    RandomAccessFile类是Java I/O库中的一个重要组件,它提供了对文件进行随机访问的能力,这意味着你可以直接跳到文件的任意位置进行读写操作,而不仅仅局限于从头到尾的顺序访问。这个类非常适合处理大数据量的文件,...

    基于RandomAccessFile文件分类的小程序

    `RandomAccessFile`是Java中的一个核心类,主要用于读写随机访问文件。与传统的`FileInputStream`和`FileOutputStream`不同,`RandomAccessFile`允许程序在文件的任意位置进行读写操作,而不是按照文件的线性顺序。...

    花1K内存实现高效I-O的RandomAccessFile类

    花1K内存实现高效I-O的RandomAccessFile类 自己搜搜这个关键字. 我主要是用来优化Android上多线程断点下载的写文件效率 RandomAccessFile是操作硬盘的,比操作内存的数据慢了几百万倍, 所有有人做出优化,我特上传...

    使用RandomAccessFile流将一个文本文件读出,并导致写入到另一个文件当中。

    在Java编程语言中,`RandomAccessFile`类提供了一种非常灵活的方式来读取和写入文件。它支持随机访问,意味着我们可以直接跳转到文件中的任何位置进行读写操作。下面我们将基于提供的代码片段,详细介绍如何利用`...

    java中文文件读写类

    最近在用java的IO包下面的RandomAccessFile类读写中文文件时遇到了乱码问题,解决不了。于是索性自己写个类,可以进行中文文件读写而不乱码。 压缩包里提供了.jar包和源代码。 ************************************...

    RandomAccessFile类

    NULL 博文链接:https://chaoyi.iteye.com/blog/2083498

    Java使用RandomAccessFile类对文件进行读写

    Java中的`RandomAccessFile`类是一个强大的文件操作工具,它允许程序员以随机访问模式读取和写入文件。与标准的`FileInputStream`和`FileOutputStream`不同,`RandomAccessFile`不仅支持顺序读写,还能直接跳转到...

    RandomAccessFile的用法

    Java的`RandomAccessFile`类是Java I/O流体系中的一种特殊类型,它允许程序员以随机访问模式读取和写入文件。与传统的输入/输出流不同,`RandomAccessFile`可以任意位置开始读写,这使得它在处理大文件或需要定位到...

    java使用RandomAccessFile类基于指针读写文件实例代码

    Java 中的 RandomAccessFile 类是一种基于指针操作的文件读写方式,能够实现对文件的随机访问。该类不同于其他许多基于流方式读写文件的类,它直接继承自 Object。 使用 RandomAccessFile 类时可以指定对要操作文件...

    Java中IO流 RandomAccessFile类实例详解

    Java中IO流 RandomAccessFile类实例详解 Java中的IO流 ...Java中的IO流 RandomAccessFile类实例详解是一个重要的知识点,掌握RandomAccessFile类的使用方法和相关技术细节,对于Java开发者来说非常重要。

    体会RandomAccessFile

    `RandomAccessFile`是Java中的一个类,位于`java.io`包中,它提供了对文件进行读写的能力,并且可以随机地定位到文件中的任何位置进行数据的读取或写入,这使得在处理大型文件时更为灵活高效。与`FileInputStream`和...

    java 随机读写Java类文件

    在Java编程中,随机读写Java类文件是一个高级主题,涉及到对字节码级别的操作,通常用于类的动态加载、代码注入或者逆向工程等场景。以下是对这一主题的详细阐述: 1. **Java类文件结构**:首先,了解Java类文件的...

    java海量数据读写优化方案

    对大数据文本文件读取(按行读取)的优化,目前常规的方案有三种,第一种LineNumberReader,第二种RandomAccessFile,第三种是内存映射文件在RandomAccessFile基础上调用getChannel().map(...);代码提供在...

    JAVA随机存储流(RandomAccessFile)的应用

    Java中的`RandomAccessFile`是一个非常重要的类,它允许我们以随机访问模式读写文件,这意味着我们可以自由地在文件的任何位置进行读写操作,而不仅仅是顺序地从头到尾处理文件。这个类提供了对文件内容进行高效定位...

    RandomAccessFile 解决乱码

    在Java编程中,`RandomAccessFile` 类提供了一种方式来处理文件中的数据,允许程序随机访问文件的任何部分。但在处理中文或其他非ASCII字符时,可能会遇到乱码问题。这是因为不同编码方式对字符的表示方式不同,如果...

    Java核心编程之文件随机读写类RandomAccessFile详解

    Java中的RandomAccessFile类是用于文件随机读写的工具类,它属于java.io包。RandomAccessFile提供了读取和写入文件的能力,并且允许程序访问文件的任意位置,这使得它能够方便地进行文件的随机访问。...

    【IT十八掌徐培成】Java基础第16天-01.RandomAccessFile.zip

    本教程聚焦于Java中的`RandomAccessFile`类,这是Java I/O流体系中的一个重要部分,用于处理可以随机读写的数据文件。在Java基础学习的第16天,我们将深入探讨这个主题。 `RandomAccessFile`类在`java.io`包下,它...

Global site tag (gtag.js) - Google Analytics