Как уменьшить вызова метода padnumber для оптимизации?

здесь я реализую вывод чисел одновременно в несколько файлов из нескольких потоков, и вот код, который генерирует номер автомобиля, в этом коде я должен его оптимизировать. Сначала я попробовал использовать Stringbuilder вне цикла, но это не улучшило производительность, а затем мне посоветовали уменьшить количество вызовов метода padNumber (пример уменьшения количества вызовов метода padNumber для регионов), что может помочь в повышении быстродействия

import java.io.IOException;
import java.io.PrintWriter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Loader implements Runnable{

    private int i;
    public Loader(int i)
    {
        this.i=i;
    }
    @Override
    public void run() {
        long start = System.currentTimeMillis();

        try {
            PrintWriter writer = new PrintWriter("res/NUMBERS " +i + ".txt");

            char letters[] = {'У', 'К', 'Е', 'Н', 'Х', 'В', 'А', 'Р', 'О', 'С', 'М', 'Т'};

            for (int regionCode = 1; regionCode <= 199; regionCode++) {

                StringBuilder stringBuilder = new StringBuilder();

                for (int number = 1; number < 1000; number++) {

                    for (char firstLetter : letters) {

                        for (char secondLetter : letters) {

                            for (char thirdLetter : letters) {
                                stringBuilder.append(firstLetter)
                                        .append(padNumber(number, 3))
                                        .append(secondLetter)
                                        .append(thirdLetter)
                                        .append(padNumber(regionCode, 2))
                                        .append("\n");
                            }
                        }
                    }
                }
                writer.write(stringBuilder.toString());
            }
            writer.flush();
            writer.close();
            System.out.println(System.currentTimeMillis() - start);
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
    private static String padNumber(int number, int numberLength) {

        String numberStr = Integer.toString(number);

        int padSize = numberLength - numberStr.length();

        for(int i=0;i<padSize;i++) {
            numberStr = '0' + numberStr;
        }

        return numberStr;
    }

    public static void main(String[] args) {
        int numOfThreads = 2;
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for(int i=0;i<numOfThreads;i++)
        {
            executorService.execute(new Loader(i));
        }
        executorService.shutdown();

        try {
            executorService.awaitTermination(10, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }


}

текущая скорость в этом коде для каждого файла составляет 60 секунд


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

Автор решения: Nowhere Man

Разумеется, следует закешировать результаты вызовов padNumber в соответствующих циклах верхнего уровня.

Однако и сам метод padNumber следует исправить, чтобы он также использовал StringBuilder, а не конкатенацию в цикле. Более того, в данном коде можно создавать и переиспользовать только один экземпляр StringBuilder заранее известной ёмкости, который можно очищать при помощи метода setLength.

Также не лишним будет напомнить о существовании конструкции try-with-resources, которая реализует автоматическое закрытие потоков ввода/вывода.

Дополнение
Имеет смысл использовать буферизированную запись при помощи BufferedWriter для повышения производительности, см. javadoc BufferedWriter.
Тогда также придётся создать экземпляр FileWriter, для непосредственной записи в файл (конструктор BufferedWriter принимает экземпяр Writer, а не строку-название файла / файл File).

Исправленный код может выглядеть так (не тестировался):

@Override
public void run() {
    long start = System.currentTimeMillis();

    try (PrintWriter writer = new PrintWriter(new BufferedWriter(
            new FileWriter("res/NUMBERS_" + i + ".txt")
        ))
    ) {
        
        char[] letters = {'У', 'К', 'Е', 'Н', 'Х', 'В', 'А', 'Р', 'О', 'С', 'М', 'Т'};
        StringBuilder sb = new StringBuilder(10 * letters.length * letters.length * letters.length); // максимальный размер буфера

        for (int regionCode = 1; regionCode <= 199; regionCode++) {
            sb.setLength(0); // очистка буфера
            // генерация кода региона, 2-3 цифры
            String region = padNumber(regionCode, 2, sb);

            for (int number = 1; number < 1000; number++) {
                sb.setLength(0); // очистка буфера
                // генерация трёхзначного номера
                String num = padNumber(number, 3, sb);
                sb.setLength(0);

                for (char firstLetter : letters) {
                    for (char secondLetter : letters) {
                        for (char thirdLetter : letters) {
                            sb
                                .append(firstLetter)
                                .append(num)
                                .append(secondLetter)
                                .append(thirdLetter)
                                .append(region)
                                .append('\n');
                        }
                    }
                }
                // печать
                writer.print(sb);
            }
        }
        System.out.println(System.currentTimeMillis() - start);
    } catch (IOException ioex) {
        ioex.printStackTrace();
    }
}

private static String padNumber(int number, int numberLength, StringBuilder sb) {
    sb.append(number);

    while (sb.length() < numberLength) {
        sb.insert(0, '0');
    }
    return sb.toString();
}
→ Ссылка