承接上文,繼續介紹優化三、四。
許多開源 DBF 庫基於流 I/O (InputStream),改用 NIO (RandomAccessFile + FileChannel + ByteBuffer) 可一次性將文件加載入內存。測試表明 NIO 比流方式快 ~5 倍。提供改進版 DBFReader (含後續優化) 下載:
核心代碼片段:
// 建立 Channel
this.dbf = new RandomAccessFile(file, "r");
this.fc = dbf.getChannel();
// 加載指定片段到 DirectByteBuffer (內核緩存區,速度更快)
private ByteBuffer loadData(int offset, int length) throws IOException {
ByteBuffer b = ByteBuffer.allocateDirect(length);
fc.position(offset);
fc.read(b);
b.rewind();
return b;
}
allocateDirect 創建 DirectBuffer,雖分銷毀較慢,但讀取更快,後續優化將解決分配開銷。
原流程:整文件讀入 ByteBuffer → 每行新建 byte[] → 封裝 Record 對象。行情文件每幾秒刷新,格式固定,行大小不變,可複用緩存。
引入 CacheManager 管理可複用的 ByteBuffer 與 byte[] 列表:
public class CacheManager {
private ByteBuffer byteBuffer; private int bufSize;
private List<byte[]> byteArrayList; private int bytesSize;
public ByteBuffer getByteBuffer(int size) {
if (bufSize < size) {
byteBuffer = ByteBuffer.allocateDirect(size + 8*1024); // 多分配避免擴容
bufSize = size + 8*1024;
}
byteBuffer.clear();
return byteBuffer;
}
public List<byte[]> getByteArrayList(int rowNum, int byteLength) {
if (bytesSize != byteLength) { byteArrayList = new ArrayList<>(); bytesSize = byteLength; }
if (byteArrayList.size() < rowNum) {
int add = rowNum - byteArrayList.size() + 100;
for (int i=0; i<add; i++) byteArrayList.add(new byte[bytesSize]);
}
return byteArrayList;
}
}
改造讀取方法 recordsWithOutDel_efficiently:
public List<byte[]> recordsWithOutDel_efficiently(CacheManager cm) throws IOException {
ByteBuffer bb = cm.getByteBuffer(getCount() * getRecordLength());
fc.position(getDataIndex()); fc.read(bb); bb.rewind();
List<byte[]> rds = new ArrayList<>(getCount());
List<byte[]> bufList = cm.getByteArrayList(getCount(), getRecordLength());
for (int i=0; i<getCount(); i++) {
byte[] b = bufList.get(i);
bb.get(b);
if ((char)b[0] != '*') rds.add(b);
}
bb.clear();
return rds; // 直接返回 byte[] 列表,避免 Record 對象創建
}
測試:緩存機制將讀取耗時從 ~5ms 降至 ~2ms,提升約 1 倍。下一步優化將針對字段解析與並行處理(見第三篇)。