Java内存映射二进制搜索

时间:2015-04-21 14:06:06

标签: java nio mmap

我目前正在尝试找到在java中搜索2GB二进制文件的最快方法。这与我的正常问题不同,因为此文件已使用mmap内存映射到Linux文件系统。

文件是一个二进制文件,我需要搜索一个固定的四字节字符串; AXL0

通常,在较小的文件上,我只是缓冲它,将其转换为字符串,然后正则表达它。但是,由于这个文件已经是内存映射的,并且非常大,重新缓冲它的想法似乎是错误的,并且将其转换为2GB字符串似乎更加冤枉...

经过一些阅读后,我发现了Java NIO包以及FileChannelsMappedByteBuffers,但我不完全确定如何设置它们。

我只需要扫描文件,从零到文件中的最后一个字节,并找到四个字节字符串的每个实例。

如果有人可以提供一些建议或意见,我会非常感激。

感谢。

1 个答案:

答案 0 :(得分:3)

抽象地看待这个任务,你不能做任何比线性搜索更好的事情。

从下面开始,使用哪种API来实际执行搜索并不重要,为简单起见,我只想使用缓冲的InputStream,它可以实现与实际数据源无关,并且没有固有的限制阻止它来自大于2GB的文件。

只要您选择合理的缓冲区大小(读​​取:不太小),您应该获得合理的性能(因为接近实际的I / O速度限制,除了可能是因为您的扫描可能需要比那种情况下的实际I / O.)

编辑:在KISS之后,你会得到几行应该做得很好的代码

public class ScanForByteCombo {

    public static List<Long> scanFor(InputStream is, int needle) throws IOException {
        List<Long> foundOffsets = new ArrayList<>();
        InputStream bs = new BufferedInputStream(is, 0x10000);
        int data = 0;
        int b;
        long offset = 0;
        while ((b = bs.read()) != -1) {
            data = (data << 8) | b;
            if (data == needle) 
                foundOffsets.add(offset);
            ++offset;
        }
        return foundOffsets;
    }

    public static void main(String[] argv) {

        int needle = ('A' << 24) | ('X' << 16) | ('F' << 8) | '0';

        long start = System.currentTimeMillis();
        try (InputStream is = new FileInputStream("your file")) {
            List<Long> found = scanFor(is, needle);
            System.out.println(found);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("scan took " + (System.currentTimeMillis() - start) + "ms. Acceptable?");
    }

}

虽然它看起来效率很低,但你可能不得不竭尽全力以值得注意的金额来提高性能。