Одновременная итерация в двух чрезвычайно больших файлах в Java

#java #memory-management #large-files

#java #управление памятью #большие файлы

Вопрос:

Мне нужно объединить два очень больших файла (> 1G каждый), записав 4 строки из первого в выходной файл, а затем записать 4 из второго. И так далее до конца. Оба файла имеют одинаковое количество строк, и это число делится на четыре. Какой наиболее эффективный способ сделать это в Java?

Комментарии:

1. Размер файла не так важен. Это то, как вы это читаете.

2. Я бы предположил, что прочитайте 4 строки из одного, запишите их, прочитайте 4 строки из другого, запишите их. Для повышения эффективности используйте буферизованные устройства чтения / записи.

3. Вероятно, более эффективно не использовать Java. Правильная комбинация инструментов командной строки Linux, вероятно, будет быстрее.

4. как насчет разделения файла на небольшие фрагменты (разделяй и властвуй)? 1. подсчитайте общее количество строк в файле 2. разделите файл на фрагменты, используя общее количество строк 3. для каждого фрагмента создайте объединенный файл. 4. объедините объединенный файл в результат.

5. @kingori, это может сработать, но это излишне сложно. Нет ничего плохого в том, чтобы просто открывать и читать оба файла последовательно; вам нужно одновременно хранить в памяти только 8 строк (по 4 из каждого файла), поэтому не имеет значения, насколько велики файлы.

Ответ №1:

Я люблю использовать Decorator шаблон. Создайте два класса, каждый класс представляет экземпляр BufferedReader.

Например,

 class First
{
   BufferedReader br;
   ...
   public String getLine()
    {
       return br.readLine();
     }
}

class Second
{
  BufferedReader br;
  ...
  public String [] getLines()
   {
     //read four lines and return it
   }
}
  

Ответ №2:

попробуйте это и посмотрите, сколько времени это займет. соответствующим образом отрегулируйте n. если это слишком медленно, попробуйте использовать nio.

 import java.io.*;
class Time {
    Time() {}
    long dt() {
        return System.currentTimeMillis() - t0;
    }
    final long t0 = System.currentTimeMillis();
}
public class Main {
    public static void create(File file1, File file2, int n) throws Exception {
        BufferedWriter bw1 = new BufferedWriter(new FileWriter(file1));
        write(bw1, n, "foo");
        bw1.close();
        BufferedWriter bw2 = new BufferedWriter(new FileWriter(file2));
        write(bw2, n, "bar");
        bw2.close();
    }
    private static void write(BufferedWriter bw, int n, String line) throws IOException {
        for (int i = 0; i < n; i  ) {
            bw.write(line);
            bw.write(lineSeparator);
        }
    }
    private static void write4(BufferedReader br1, BufferedWriter bw, String line) throws IOException {
        bw.write(line);
        bw.write(lineSeparator);
        for (int i = 0; i < 3; i  ) {
            line = br1.readLine();
            bw.write(line);
            bw.write(lineSeparator);
        }
    }
    public static void main(String[] args) throws Exception {
        File file1 = new File("file1");
        File file2 = new File("file2");
        if (!file1.exists()) {
            create(file1, file2, 10000000);
        }
        File file3 = new File("file3");
        Time time=new Time();
        BufferedReader br1 = new BufferedReader(new FileReader(file1));
        BufferedReader br2 = new BufferedReader(new FileReader(file2));
        BufferedWriter bw = new BufferedWriter(new FileWriter(file3));
        String line1, line2;
        while ((line1 = br1.readLine()) != null) {
            write4(br1, bw, line1);
            line2 = br2.readLine();
            write4(br2, bw, line2);
        }
        br1.close();
        br2.close();
        bw.close();
        System.out.println(time.dt()/1000. " s.");
    }
    static final String lineSeparator = System.getProperty("line.separator");
}
  

Ответ №3:

Извините, если эта идея уже была дана 🙂

Я думаю, что самый быстрый способ сделать это — создать 3 потока. t1 и t2 будут считывать данные из двух входных файлов, у каждого потока будет своя очередь (потокобезопасная), и поток прочитает 4 строки и поместит их в свою очередь. t3 будет потоком записи, он будет взаимозаменяемо считывать узлы из двух очередей и помещать их в новый объединенный файл. Я думаю, что это хорошее решение, потому что оно позволяет выполнять параллельный ввод-вывод для всех трех файлов (а ввод-вывод — это горлышко бутылки здесь ..) и минимальное взаимодействие между потоками.