如果您已经有通过文件读取一次找到钥匙的指数,绝对速度最快的解决办法是读线并记住它们。如果由于某些原因(例如内存限制)不起作用,使用缓冲区确实是一个很好的选择。这是代码大纲:
FileChannel channel = new RandomAccessFile("/some/file", "r").getChannel();
long pageSize = ...; // e.g. "3 GB or file size": max(channel.size(), THREE_GB);
long position = 0;
ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, position, pageSize);
ByteBuffer slice;
int maxLineLength = 30;
byte[] lineBuffer = new byte[maxLineLength];
// Read line at indices 20 - 25
buffer.position(20);
slice = buffer.slice();
slice.get(lineBuffer, 0, 6);
System.out.println("Starting at 20:" + new String(lineBuffer, Charset.forName("UTF8")));
// Read line at indices 0 - 10
buffer.position(0);
slice = buffer.slice();
slice.get(lineBuffer, 0, 11);
System.out.println("Starting at 0:" + new String(lineBuffer, Charset.forName("UTF8")));
此代码也可以用于非常大的文件。只需拨打channel.map
找到“页”里你的关键所在位置:position = keyIndex/pageSize * pageSize
,然后调用buffer.position
从指数:keyIndex - position
如果你真的没有任何办法组获得一“页”在一起,那么你不需要slice
。业绩不会好,但是这可以让你简化代码进一步:
byte[] lineBuffer = new byte[maxLineLength];
// ...
ByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, keyIndex, lineLength);
buffer .get(lineBuffer, 0, lineLength);
System.out.println(new String(lineBuffer, Charset.forName("UTF8")));
注意,ByteBuffer
未在JVM堆上创建,但实际上是在操作系统级别内存映射文件。 (从Java 8开始,您可以通过查看源代码并在实现中搜索sun.nio.ch.DirectBuffer
来验证这一点)。
线尺寸:获得行大小的最好方法是储存它,当你通过文件扫描,即使用Map[String, (Long, Int)]
,而不是现在使用的是什么index
。如果不为你工作,你应该运行一些测试,以找出什么是快:
- 只存储最大行的大小,然后搜索这个最大长度的字符串中的换行。在这种情况下,请注意您覆盖了在单元测试中访问文件的末尾。
- 用
ByteBuffer.get
继续扫描,直至遇到\n
。如果您有真正的Unicode文件,那么这可能不是一种选择,因为换行的ASCII码(0x0A)可以出现在其他地方,例如UTF-16编码的韩文音节中带有字符代码0xAC0A。
这将是第二个方法的Scala代码:
// this happens once
val maxLineLength: Long = 2000 // find this in your initial sequential scan
val lineBuffer = new Array[Byte](maxLineLength.asInstanceOf[Int])
// this is how you read a key
val bufferLength = maxLineLength min (channel.size() - index("key"))
val buffer = channel.map(FileChannel.MapMode.READ_ONLY, index("key"), bufferLength)
var lineLength = 0 // or minLineLength
while (buffer.get(lineLength) != '\n') {
lineLength += 1
}
buffer.get(lineBuffer, 0, lineLength - 1)
println(new String(lineBuffer, Charset.forName("UTF8")))
是不是访问到不同的文件?如果没有,为什么你关闭文件访问?如果保持文件访问权限不变,则不必等待操作系统授予您读取权限。 – Tschallacka
@Tschallacka我只是在所有阅读结束时才结束,这只是一个例子。但是我的问题在于读取文件的方式。 –
您能否提供索引阅读的代码以及如何将其翻译为查找位置?由于您已经走上了一条良好的道路,您的索引查找可能会从一些优化中受益,但是如果没有完整的代码和示例数据,则很难提供帮助。 – Tschallacka