如果您已經有通過文件讀取一次找到鑰匙的指數,絕對速度最快的解決辦法是讀線並記住它們。如果由於某些原因(例如內存限制)不起作用,使用緩衝區確實是一個很好的選擇。這是代碼大綱:
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