StringBuffer против обычной печати - PullRequest
0 голосов
/ 20 апреля 2020

Почему печать элементов массива в al oop занимает больше времени по сравнению с добавлением всех элементов массива в StringBuffer и последующей печатью?

В этом я напечатал элементы массива в l oop.

public static void main (String[] args) {
    Scanner scan=new Scanner(System.in);
    int count=scan.nextInt();
    for (int i=0;i<count;i++) {
        int arrlen=scan.nextInt();
        int rotate=scan.nextInt();
        int revarr[]=new int[arrlen];
        for (int j=0;j<arrlen;j++) {
            revarr[(arrlen-rotate+j)%arrlen]=scan.nextInt();
        }
        for (int j:revarr) {
            System.out.print(j+" ");
        }   
        System.out.println();
    }
}

В этом я добавил элементы массива в StringBuffer, а затем напечатал.

public static void main (String[] args) {
    Scanner scan=new Scanner(System.in);
    int count=scan.nextInt();
    for (int i=0;i<count;i++) {
        int arrlen=scan.nextInt();
        int rotate=scan.nextInt();
        int revarr[]=new int[arrlen];
        for(int j=0;j<arrlen;j++){
            revarr[(arrlen-rotate+j)%arrlen]=scan.nextInt();
        }
        StringBuffer s = new StringBuffer();
        for (int j:revarr){
            s.append(j+" ");
        }   
        System.out.println(s);
    }
}

Ответы [ 3 ]

1 голос
/ 20 апреля 2020

Вероятная причина заключается в реализации PrintStream, для которого System.out установлено (по умолчанию).

В Java 11 метод, создающий PrintStream, имеет вид следующим образом:

private static PrintStream newPrintStream(FileOutputStream fos, String enc) {
   if (enc != null) {
        try {
            return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);
        } catch (UnsupportedEncodingException uee) {}
    }
    return new PrintStream(new BufferedOutputStream(fos, 128), true);
}

Ключевая строка такова:

return new PrintStream(new BufferedOutputStream(fos, 128), true, enc);

Во-первых, создается BufferedOutputStream с размером буфера 128 байтов. Это мало. Если вы выполняете небольшую запись в System.out, будет буфер flu sh , по крайней мере, каждые 128 байт.

Во-вторых, параметр true включает автоматическую очистку. Javado c описывает это следующим образом:

"Если true, выходной буфер будет очищаться всякий раз, когда записывается байтовый массив, вызывается один из методов println или символ новой строки или байт ('\n') пишется "

Опять же, это означает, что есть больше смыва.

Почему это имеет значение?

Очистка скважины включает в себя выполнение системного вызова fwrite для записи символов. Системные вызовы относительно дороги. Согласно некоторым числам, которые я видел, издержки системного вызова fwrite системного вызова составляют порядка от 60 до 350 нс. Это не так уж и много, но если вы сделаете это несколько раз за l oop итераций по сравнению с одним разом за l oop итераций, и повторяете это достаточно долго, разница может быть значительной.

Могут также быть издержки, которые зависят от того, к чему подключен System.out. Например, если вы выполняете запись в консоль, большое количество мелких записей может замедлить работу консольного приложения.


Возможно другое объяснение. Код теста, который вы показали, не учитывает возможные эффекты разогрева JVM. Например, может быть, что один пример запускает JIT-компиляцию, а другой нет. Издержки компиляции JIT могут привести к тому, что первое займет больше времени, чем второе.

0 голосов
/ 20 апреля 2020

Очень часто операции ввода и вывода занимают много времени в Java. Каждый раз, когда вы вызываете print или println, он генерирует дополнительные издержки, которые занимают гораздо больше времени, чем фактическая печать, поэтому на самом деле гораздо быстрее распечатать все за один пакет.
Этот метод используется print и println:

    private void write(String s) {
        try {
            synchronized(this) {
                this.ensureOpen();
                this.textOut.write(s);
                this.textOut.flushBuffer();
                this.charOut.flushBuffer();
                if (this.autoFlush && s.indexOf(10) >= 0) {
                    this.out.flush();
                }
            }
        } catch (InterruptedIOException var5) {
            Thread.currentThread().interrupt();
        } catch (IOException var6) {
            this.trouble = true;
        }

    }

Как видите, это синхронизированная операция. Синхронизация в Java снижает производительность, поскольку для получения и снятия блокировок требуется много времени, а код в синхронизированном блоке не оптимизируется компилятором.

Вы могли бы получить еще более быстрое время, если бы использовали StringBuilder вместо StringBuffer, так как оно не синхронизировано, и у вас будут меньше накладные расходы.

0 голосов
/ 20 апреля 2020

Печать в System.out является довольно длительной операцией, и добавление в StringBuffer происходит быстро. StringBuffer предназначен для объединения строк. Однако StringBuffer устарел. Вместо этого используйте StringBuilder

...