Оптимизация кода, считывание данных построчно

помогите оптимизировать код. Мой код работает очень медленно на больших объемах данных. Проблема: У меня есть входящий файл со временем работы сервера, мне нужно достать из него данные, когда сервер не работал и записать их в другой файл. Старшие товарищи рекомендуют считывать файл построчно и обрабатывать его построчно. Я же, видимо, обрабатываю весь файл сразу, что очень сказывается на производительности. Помогите разобраться в вопросе, что я делаю не так? Заранее благодарю)

public static void unavailable(String source, String target) {
        try (BufferedReader reader = new BufferedReader(new FileReader(source));
            PrintWriter writer = new PrintWriter(new BufferedOutputStream(new FileOutputStream(target)))) {
                String serverDown = null;
                while (reader.ready()) {
                    String status = reader.readLine();
                    if (serverDown == null && (status.startsWith("400") || status.startsWith("500"))) {
                        writer.println(status.split(" ")[1] + " server down");
                        serverDown = status;
                    } else if (serverDown != null && (!status.startsWith("400") || !status.startsWith("500"))) {
                        writer.println(status.split(" ")[1] + " server up");
                        serverDown = null;
                    }
                }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Ответы (3 шт):

Автор решения: Sergey Chernikov

Если я правильно понял ваш вопрос, то ниже фрагмент кода, который предназначен для чтения файла построчно с помощью Stream API.

{
  List<String> rows = Files.lines(Paths.get(/*source path*/))
  .collect(Collectors.toList());

  rows.forEach(/*performs the needed operations*/);
}
→ Ссылка
Автор решения: lampa

Как вариант:

package com.lampa;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.concurrent.*;

public class SuperApp {
    public static void main(String... args) throws IOException, InterruptedException {
        long startTime = System.nanoTime();

        //App.run(args);
        // Размер читаемого чанка из файла
        final int chunkSize = 512 * 1024 * 50;

        // Размер чанка для поиска переноса
        final int breakLineFindBlockSize = 512 * 1024;

        // Количество одновременно работающих потоков поиска по слову
        // Блокировка потоков чтения от переполнения памяти.
        // Потребление программы == chunkSize * threadSize
        final int threadSize = 16;
        final Semaphore lock = new Semaphore(threadSize);
        final ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(threadSize);

        File input = new File("C:\\javafx\\warn.txt");
        File output = new File("C:\\javafx\\warn2.txt");

        PrintWriter writer = new PrintWriter(new BufferedOutputStream(new FileOutputStream(output)));
        RandomAccessFile randomAccessFile = new RandomAccessFile(input, "r");
        FileChannel fc = randomAccessFile.getChannel();

        long correct = 0; //
        long fileSize = input.length();
        ByteBuffer chunkBuffer;
        ByteBuffer bufferLineFind;
        long finalSize = 0;

        // Начинаем читать файл в чанки
        while (correct < fileSize) {
            ByteArrayOutputStream finalBuffer = new ByteArrayOutputStream();

            chunkBuffer = ByteBuffer.allocate((correct + chunkSize > fileSize) ? (int) (fileSize - correct) : chunkSize);
            fc.position(correct);
            fc.read(chunkBuffer);
            finalBuffer.write(chunkBuffer.array());

            correct += chunkBuffer.capacity();

            // Выполняем поиск переноса в чанке поиска переноса
            loop: while (correct < fileSize) {
                bufferLineFind = ByteBuffer.allocate(breakLineFindBlockSize); //new byte[breakLineFindBlockSize];
                fc.position(correct);
                fc.read(bufferLineFind);

                for (int g = 0; g < breakLineFindBlockSize; g++) {
                    if (bufferLineFind.get(g) == '\n') {
                        correct += g;

                        byte[] array = new byte[g];
                        System.arraycopy(bufferLineFind.array(), 0, array, 0, g);

                        finalBuffer.write(array);

                        break loop;
                    }
                }

                correct += breakLineFindBlockSize;
                finalBuffer.write(bufferLineFind.array());
            }

            // Реализация макс количества потоков
            lock.acquire();

            executor.execute(() -> {

                try {
                    //File newFile = new File("C:\\javafx\\application_" + finalBuffer.size());
                    //Files.write(newFile.toPath(), finalBuffer.toByteArray());

                    BufferedReader bfReader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(finalBuffer.toByteArray())));
                    String temp = null;
                    while ((temp = bfReader.readLine()) != null) {
                        if (temp.contains("WARN")) {
                            writer.write(temp + "\n");
                        }
                    }

                } catch (IOException ex) {
                    ex.printStackTrace();
                } finally {
                    //System.out.println("chunk size: " + finalBuffer.size());
                    lock.release();
                }
            });

            finalSize += finalBuffer.size();
        }

        if (executor.getActiveCount() > 0) {
            executor.shutdown();
            executor.awaitTermination(60, TimeUnit.SECONDS);
        }

        writer.close();
        System.out.println("final : " + finalSize + ":" + input.length());


        long endTime = System.nanoTime();
        long totalTime = endTime - startTime;

        System.out.println("Execution time is " + TimeUnit.NANOSECONDS.toMillis(totalTime) + "ms");
    }
}
→ Ссылка
Автор решения: Dmitry Chizhov

В итоге оказалось, что надо было развести потоки по разным методам, что бы поток BufferedReader не висел открытым. Метод toFile записывает в файл уже обработанный список, в котором находятся только те данные, которые нам нужны не перегружая при этом систему. Всем спасибо за помощь. Не обращайте внимания на имя класса, это сделано намеренно, что бы избежать дубликатов в проекте)

Вот код который приняли:

public class Analizy {

private static List<String> resultList = new ArrayList<>();

/**
 * Method "toFile()" - implements writing the list to a file
 * @param target - path to the file to write
 */

private static void toFile(String target) {
    try (PrintWriter writer = new PrintWriter(
            new BufferedOutputStream(
                    new FileOutputStream(target)
            ))) {
        resultList.forEach(data -> writer.write(data + System.lineSeparator()));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

/**
 * Server unavailability statistics
 *
 * @param source data provider file address
 * @param target file address for recording statistics
 */

public static void unavailable(String source, String target) throws IOException {
    String separate = System.lineSeparator();
    StringBuilder build = new StringBuilder();
        try (BufferedReader reader = new BufferedReader(new FileReader(source))) {
            String line = reader.readLine();
            while (line != null) {
                if (line.startsWith("400") || line.startsWith("500")) {
                   build.append(line.substring(4)).append(" - ");
                    resultList.add(line.split(" ")[1] + " server down");
                    while (line.startsWith("400") || line.startsWith("500") || line.isEmpty()) {
                        line = reader.readLine();
                    }
                    build.append(line.substring(4)).append(separate);
                    resultList.add(line.split(" ")[1] + " server up");
                }
                line = reader.readLine();
                build = new StringBuilder();
            }
        }
    toFile(target);
    }
}
→ Ссылка