Java - 读写大型长数组代码

时间:2012-08-15 20:24:25

标签: java

我正在尝试将一个大小为400000000的大型长数组写入文件,然后将其读回。我正在使用的代码如下:

import java.io.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.io.RandomAccessFile ;
import java.util.* ;

class Checks {
public static FileChannel channel;
public static MappedByteBuffer mbb;

public static void main(String[] args){
    try{


      long k[] = new long[400000000] ;
          for(int i = 0 ; i < 400000000 ; i++){
            k[i] = i ;
          }

    channel = new RandomAccessFile("abc.dat", "rw").getChannel();
        mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, 1 << 24);
        mbb.order(ByteOrder.nativeOrder());

          for(int i = 0 ; i < 400000000 ;i++ ){
             getMbb().putLong(k[i]);
        }

        channel.close();

       long ks[] = new long[400000000] ;

        channel = new RandomAccessFile("abc.dat", "rw").getChannel();
        mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, 1 << 24);
        mbb.order(ByteOrder.nativeOrder());

        for(int r = 0 ; r < 400000000; r++){
            ks[r] = getMbb().getLong();
         }

             for(int r = 0 ; r < 400000000; r++){
                 if(k[r] != ks[r]){
                  System.out.println("Error at " + r);
                  break ;
                  }
              }


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

   }

    public static ByteBuffer getMbb() throws IOException {
        if (mbb.remaining() <= 0) {
            mbb = channel.map(FileChannel.MapMode.READ_WRITE, channel.size(), 1 << 24);
            mbb.order(ByteOrder.nativeOrder());
        }
        return mbb;
    }
}

但是,此代码给出的错误是写入和读取数组不相同。任何人都可以帮助我为什么会这样?

4 个答案:

答案 0 :(得分:2)

在关闭频道之前,尝试强制对映射缓冲区所做的更改到磁盘:

 mbb.force();

这样,如果您使用的是存储在本地设备上的文件,您可以保证对映射缓冲区所做的所有更改都将反映在磁盘上。

考虑到你只附加到文件,另一种完成你想要的方法是:

  channel = new RandomAccessFile("abc.dat", "rw").getChannel();

  ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE/8 * 1001);
  for(int i = 0 ; i < 400000000 ;i++ ){
     //write a number of byte to a ByteBuffer
      buffer.putLong(k[i]);
      if(i % 1000 == 0){
          channel.write(buffer);
          buffer.clear();
      }
   }
   channel.write(buffer);
   buffer.clear();

阅读:

    buffer.clear();
    int bytesCnt;
    while((bytesCnt = channel.read(buffer))!=-1){
    for(int r = 0 ; r < bytesCnt; r++){
        ks[r] = buffer.getLong();
     }
    }

关于性能:在执行I / O的应用程序中,性能损失是由于搜索次数所致。因此,寻求次数越少,性能越高。这相当于在顺序中写入尽可能多的数据(&lt; =&lt; =&gt;较小的刷新次数;一个刷新均值,实际上是一个搜索)方式。

在您的方案中,数据仅以顺序方式写入(附加到文件),因此唯一需要担心的是刷新到磁盘的次数;该数字与缓冲区的大小成反比。所以你应该尽量增加缓冲区的大小。

答案 1 :(得分:2)

以下是我的建议:

public static void main(String[] args) {

  File f = new File("abc.txt");

  DataOutputStream s = new DataOutputStream( new FileOutputStream(f));

  for ( int i = 0; i < 400000000; i++ ) {
    s.writeLong(i);
  }
  s.close();

  DataInputStream is = new DataInputStream( new FileInputStream(f));
  for (int i = 0; i < 400000000; i++ ) {
    if ( i != is.readLong() ) System.out.println("Error at " + i);
  }
}

这可以完成您的所有程序,但不会显式分配内存,当然也不会通过内存映射缓冲区重复分配。 如果这个解决方案实际上适用于你真正想要做的事情很难说,因为你没有说明这是什么。

答案 2 :(得分:1)

我修改了一下您的代码以使用DataInputStreamDataOutputStream而不是RandomAccessFile,它就像魅力一样。

    try {
        long k[] = new long[400000000];
        for (int i = 0; i < k.length; i++) {
            k[i] = i;
        }

        DataOutputStream dos = new DataOutputStream(new FileOutputStream("abc.dat"));
        for (int i = 0; i < k.length; i++) {
            dos.writeLong(k[i]);
        }

        long ks[] = new long[k.length];

        DataInputStream dis = new DataInputStream(new FileInputStream("abc.dat"));

        for (int r = 0; r < ks.length; r++) {
            ks[r] = dis.readLong();
        }

        for (int r = 0; r < k.length; r++) {
            if (k[r] != ks[r]) {
                System.out.println("Error at " + r);
                break;
            }
        }
    } catch(Exception e) {
    } finally {
        // Make sure to close the streams
        dos.close();
        dis.close();
    }

答案 3 :(得分:1)

我认为您的getMbb()方法已被破坏。每次重新映射内存中文件的块时,都会映射到channel.size()。这仅在您创建文件时有效,但在您阅读时无效。当您阅读文件时,您将映射文件末尾之后来的文件的“区域”,并且该文件具有随机内容。

您必须修复重新映射代码,以便跟踪文件中的位置。