我正在尝试将一个大小为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;
}
}
但是,此代码给出的错误是写入和读取数组不相同。任何人都可以帮助我为什么会这样?
答案 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)
我修改了一下您的代码以使用DataInputStream和DataOutputStream而不是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()
。这仅在您创建文件时有效,但在您阅读时无效。当您阅读文件时,您将映射在文件末尾之后来的文件的“区域”,并且该文件具有随机内容。
您必须修复重新映射代码,以便跟踪文件中的位置。