将大量小文件读入内存的最快方法是什么?

2022-09-02 03:21:04

我需要在每个服务器上读取大约50个文件,并将每个文本文件的表示形式放入内存中。每个文本文件都有自己的字符串(哪个是用于字符串持有者的最佳类型?)。

将文件读入内存的最快方法是什么,保存文本的最佳数据结构/类型是什么,以便我可以在内存中对其进行操作(主要是搜索和替换)?

谢谢


答案 1

内存映射文件将是最快的...像这样:

    final File             file;
    final FileChannel      channel;
    final MappedByteBuffer buffer;

    file    = new File(fileName);
    fin     = new FileInputStream(file);
    channel = fin.getChannel();
    buffer  = channel.map(MapMode.READ_ONLY, 0, file.length());

然后继续从字节缓冲区读取。

这将比 或 快得多。FileInputStreamFileReader

编辑:

经过一些调查,事实证明,根据您的操作系统,您可能最好使用新的操作系统。然而,将整个事情一次全部读入文件大小的char[]听起来像是最糟糕的方式。BufferedInputStream(new FileInputStream(file))

因此,应该在所有平台上提供大致一致的性能,而内存映射文件可能会很慢或很快,具体取决于底层操作系统。与性能至关重要的所有内容一样,您应该测试代码,看看哪种方法效果最好。BufferedInputStream

编辑:

好的,这里有一些测试(第一个测试完成两次以使文件进入磁盘缓存)。

我在rt.jar类文件上运行它,提取到硬盘驱动器,这是在Windows 7 beta x64下。这是16784个文件,总共94,706,637字节。

首先是结果...

(请记住,重复第一个以获得磁盘缓存设置)

  • 数组测试

    • 时间 = 83016
    • 字节 = 118641472
  • 数组测试

    • 时间 = 46570
    • 字节 = 118641472
  • DataInputByteAtATime

    • 时间 = 74735
    • 字节 = 118641472
  • DataInputReadfully

    • 时间 = 8953
    • 字节 = 118641472
  • 内存映射

    • 时间 = 2320
    • 字节 = 118641472

这是代码...

import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.HashSet;
import java.util.Set;

public class Main
{
    public static void main(final String[] argv)
    {
        ArrayTest.main(argv);
        ArrayTest.main(argv);
        DataInputByteAtATime.main(argv);
        DataInputReadFully.main(argv);
        MemoryMapped.main(argv);
    }
}

abstract class Test
{
    public final void run(final File root)
    {
        final Set<File> files;
        final long      size;
        final long      start;
        final long      end;
        final long      total;

        files = new HashSet<File>();
        getFiles(root, files);

        start = System.currentTimeMillis();

        size = readFiles(files);

        end = System.currentTimeMillis();
        total = end - start;

        System.out.println(getClass().getName());
        System.out.println("time  = " + total);
        System.out.println("bytes = " + size);
    }

    private void getFiles(final File      dir,
                          final Set<File> files)
    {
        final File[] childeren;

        childeren = dir.listFiles();

        for(final File child : childeren)
        {
            if(child.isFile())
            {
                files.add(child);
            }
            else
            {
                getFiles(child, files);
            }
        }
    }

    private long readFiles(final Set<File> files)
    {
        long size;

        size = 0;

        for(final File file : files)
        {
            size += readFile(file);
        }

        return (size);
    }

    protected abstract long readFile(File file);
}

class ArrayTest
    extends Test
{
    public static void main(final String[] argv)
    {
        final Test test;

        test = new ArrayTest();
        test.run(new File(argv[0]));
    }

    protected long readFile(final File file)
    {
        InputStream stream;

        stream = null;

        try
        {
            final byte[] data;
            int          soFar;
            int          sum;

            stream = new BufferedInputStream(new FileInputStream(file));
            data   = new byte[(int)file.length()];
            soFar  = 0;

            do
            {
                soFar += stream.read(data, soFar, data.length - soFar);
            }
            while(soFar != data.length);

            sum = 0;

            for(final byte b : data)
            {
                sum += b;
            }

            return (sum);
        }
        catch(final IOException ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            if(stream != null)
            {
                try
                {
                    stream.close();
                }
                catch(final IOException ex)
                {
                    ex.printStackTrace();
                }
            }
        }

        return (0);
    }
}

class DataInputByteAtATime
    extends Test
{
    public static void main(final String[] argv)
    {
        final Test test;

        test = new DataInputByteAtATime();
        test.run(new File(argv[0]));
    }

    protected long readFile(final File file)
    {
        DataInputStream stream;

        stream = null;

        try
        {
            final int fileSize;
            int       sum;

            stream   = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
            fileSize = (int)file.length();
            sum      = 0;

            for(int i = 0; i < fileSize; i++)
            {
                sum += stream.readByte();
            }

            return (sum);
        }
        catch(final IOException ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            if(stream != null)
            {
                try
                {
                    stream.close();
                }
                catch(final IOException ex)
                {
                    ex.printStackTrace();
                }
            }
        }

        return (0);
    }
}

class DataInputReadFully
    extends Test
{
    public static void main(final String[] argv)
    {
        final Test test;

        test = new DataInputReadFully();
        test.run(new File(argv[0]));
    }

    protected long readFile(final File file)
    {
        DataInputStream stream;

        stream = null;

        try
        {
            final byte[] data;
            int          sum;

            stream = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
            data   = new byte[(int)file.length()];
            stream.readFully(data);

            sum = 0;

            for(final byte b : data)
            {
                sum += b;
            }

            return (sum);
        }
        catch(final IOException ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            if(stream != null)
            {
                try
                {
                    stream.close();
                }
                catch(final IOException ex)
                {
                    ex.printStackTrace();
                }
            }
        }

        return (0);
    }
}

class DataInputReadInChunks
    extends Test
{
    public static void main(final String[] argv)
    {
        final Test test;

        test = new DataInputReadInChunks();
        test.run(new File(argv[0]));
    }

    protected long readFile(final File file)
    {
        DataInputStream stream;

        stream = null;

        try
        {
            final byte[] data;
            int          size;
            final int    fileSize;
            int          sum;

            stream   = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
            fileSize = (int)file.length();
            data     = new byte[512];
            size     = 0;
            sum      = 0;

            do
            {
                size += stream.read(data);

                sum = 0;

                for(int i = 0; i < size; i++)
                {
                    sum += data[i];
                }
            }
            while(size != fileSize);

            return (sum);
        }
        catch(final IOException ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            if(stream != null)
            {
                try
                {
                    stream.close();
                }
                catch(final IOException ex)
                {
                    ex.printStackTrace();
                }
            }
        }

        return (0);
    }
}
class MemoryMapped
    extends Test
{
    public static void main(final String[] argv)
    {
        final Test test;

        test = new MemoryMapped();
        test.run(new File(argv[0]));
    }

    protected long readFile(final File file)
    {
        FileInputStream stream;

        stream = null;

        try
        {
            final FileChannel      channel;
            final MappedByteBuffer buffer;
            final int              fileSize;
            int                    sum;

            stream   = new FileInputStream(file);
            channel  = stream.getChannel();
            buffer   = channel.map(MapMode.READ_ONLY, 0, file.length());
            fileSize = (int)file.length();
            sum      = 0;

            for(int i = 0; i < fileSize; i++)
            {
                sum += buffer.get();
            }

            return (sum);
        }
        catch(final IOException ex)
        {
            ex.printStackTrace();
        }
        finally
        {
            if(stream != null)
            {
                try
                {
                    stream.close();
                }
                catch(final IOException ex)
                {
                    ex.printStackTrace();
                }
            }
        }

        return (0);
    }
}

答案 2

最有效的方法是:

  • 确定文件的长度 (File.length())
  • 创建大小相同(或稍大)的 char 缓冲区
  • 确定文件的编码
  • 用于阅读new InputStreamReader (new FileInputStream(file), encoding)
  • 通过对 read() 的单个调用将 while 文件读入缓冲区。请注意,read() 可能会提前返回(没有读取整个文件)。在这种情况下,请再次调用它,并带有偏移量以读取下一批。
  • 创建字符串:new String(buffer)

如果您需要在启动时搜索和替换一次,请使用String.replaceAll()。

如果你需要重复做,你可以考虑使用StringBuilder。它没有 replaceAll(),但您可以使用它来操作字符数组(->不分配内存)。

可是:

  1. 使代码尽可能简短。
  2. 衡量性能
  3. 它太慢了,修复它。

如果只需0.1秒即可执行,则没有理由浪费大量时间来使此代码快速运行。

如果仍然有性能问题,请考虑将所有文本文件放入 JAR 中,将其添加到类路径中,然后使用 Class.getResourceAsStream() 读取文件。从 Java 类路径加载内容是高度优化的。


推荐