有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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) 个答案

  1. # 1 楼答案

    我觉得你的getMbb()方法坏了。每次在内存中重新映射文件块时,都会映射到channel.size()。这只在创建文件时有效,但在读取文件时无效。在读取文件时,映射文件末尾之后的文件“区域”,该区域包含随机内容

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

  2. # 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);
      }
    }
    

    这是程序的全部功能,但不需要显式分配内存,当然也不需要通过内存映射缓冲区重复分配。 如果这个解决方案真的适用于你真正想做的事情,很难说,因为你没有说这是为了什么

  3. # 3 楼答案

    在关闭通道之前,请尝试强制对映射到磁盘的缓冲区进行更改:

     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的应用程序中,性能损失取决于搜索次数。因此,搜索次数越少,性能越高。这相当于以顺序的方式将尽可能多的数据写入(<;=>;较小的磁盘刷新次数;实际上,刷新意味着查找)

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

  4. # 4 楼答案

    我对你的代码做了一些修改,用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();
        }