Создать строку с n символов


144

Есть ли способ в Java, чтобы создать строку с указанным номером указанного символа? В моем случае мне нужно создать строку с 10 пробелами. Мой текущий код:

StringBuffer outputBuffer = new StringBuffer(length);
for (int i = 0; i < length; i++){
   outputBuffer.append(" ");
}
return outputBuffer.toString();

Есть ли лучший способ сделать то же самое. В частности, я хотел бы что-то, что быстро (с точки зрения исполнения).


1
Если вы делаете это много, просто напишите функцию: String characterRepeat (Char c, int Length) {...}, которая делает то, что вы делаете, для любого символа и любой длины. Тогда просто позвоните, когда вам это нужно.
Остин Фицпатрик

12
вы хотите использовать StringBuilder вместо

Добавить в начале размер буфера, легко рассчитать облегчает вещи в управлении памятью !. StringBuilder outputBuffer = new StringBuilder (repeat * base.length ());
Виктор


Если вы хотите добавить один пробел, используйте append(' ')вместо этого ... это требует немного меньше вычислений ...
Эрк

Ответы:


36

Цикл for будет оптимизирован компилятором. В таких случаях, как ваш, вам не нужно заботиться об оптимизации самостоятельно. Доверься компилятору.

Кстати, если есть способ создать строку с n пробелами, то она закодирована так же, как вы только что это сделали.


1
даже если это не было оптимизировано - как часто это создается в вашей программе - если часто сохраняются в статической переменной или другом кэше
мммммм

Кстати, Array.fill () просто перебирает массив. / Мне нужно определенно больше очков, чтобы комментировать другие сообщения :)
kalkin

Длина @Mark является переменной, поэтому глупо сохранять ее. Что бы я сделал, есть static Dictionary<int, String>?
К. Росс

1
Сохраните самую длинную строку, которую вы когда-либо захотите, а затем используйте что-то вроде этого: " ".substring(0, 10);
mjaggard

169

Вероятно, самый короткий код с использованием StringAPI, исключительно:

String space10 = new String(new char[10]).replace('\0', ' ');

System.out.println("[" + space10 + "]");
// prints "[          ]"

Как метод, без непосредственного создания экземпляра char:

import java.nio.CharBuffer;

/**
 * Creates a string of spaces that is 'spaces' spaces long.
 *
 * @param spaces The number of spaces to add to the string.
 */
public String spaces( int spaces ) {
  return CharBuffer.allocate( spaces ).toString().replace( '\0', ' ' );
}

Вызвать используя:

System.out.printf( "[%s]%n", spaces( 10 ) );

4
Несмотря на то, что мне нравится однострочник, я думаю, что решение FrustratedWithFormsDes лучше, когда дело доходит до выполнения, поскольку оно избегает проверки каждого на \ 0 и просто назначает пространство.
Bouncner

1
Протестировано, это всегда быстрее, чем цикл, довольно последовательно на 50000-100000 наносекунд быстрее, что может быть довольно значительным (0,1 миллисекунды), цикл действительно ускоряется, так как количество итераций увеличивается, хотя общее время все еще остается высоким. Это справедливо при создании различных размеров космических строк.
kmecpp

спасибо за хорошее решение, я использовал его в своем ответе здесь stackoverflow.com/questions/852665/…
maytham-ɯɐɥʇʎɐɯ

66

Хм, теперь, когда я думаю об этом, может быть Arrays.fill:

char[] charArray = new char[length];
Arrays.fill(charArray, ' ');
String str = new String(charArray);

Конечно, я предполагаю, что fillметод делает то же самое, что и ваш код, поэтому он, вероятно, будет работать примерно так же, но, по крайней мере, это меньше строк.


2
После проверки источника, кажется , что это делает на самом деле делать то , что отправил OP: строка 806 docjar.com/html/api/java/util/Arrays.java.html
Pops

2
@ Лорд Торгамус Был ли отредактирован вопрос ОП? Потому что в версии, которую я вижу, он зацикливается на StringBuffer.append (), а версия Frustrated выполняет заливку (которая, конечно, зацикливает выполнение назначений char массиву). Совсем не одно и то же.
CPerkins

@Perkins, честно говоря, мне было непонятно. Моя точка зрения заключается в том, что они оба выполняют посимвольную вставку внутри цикла for.
Pops

1
@ Лорд Торгамус - Согласен на это. Жаль, что JVM не может просто сделать memset. Цену мы платим за широкие символы.
CPerkins

2
@Pops, стоит отметить, что если Arrays.fill()функция используется последовательно для выполнения этой работы в вашем коде и (стандартном и внешнем) коде библиотеки, то она является хорошим кандидатом для компиляции Hotspot и дает больше шансов найти ваш кэш ЦП. Будущие JVM могут назначить его как встроенную функцию. Роллинг сам по себе отрезает вас от всего этого совершенства в исполнении ...
SusanW

65

Я настоятельно рекомендую не писать цикл вручную. Вы будете делать это снова и снова в течение своей карьеры программиста. Люди, читающие ваш код, включая вас, всегда должны тратить время, даже если это всего лишь несколько секунд, чтобы переварить смысл цикла.

Вместо того, чтобы повторно использовать один из доступных библиотек , обеспечивающих код , который делает именно это , как StringUtils.repeatиз Apache Commons Lang :

StringUtils.repeat(' ', length);

Таким образом, вам также не нужно беспокоиться о производительности, поэтому все подробности StringBuilder, оптимизация компилятора и т. Д. Скрыты. Если функция будет работать медленно, это будет ошибкой библиотеки.

С Java 11 это становится еще проще:

" ".repeat(length);

5
С другой стороны, если StringUtils еще не используется в проекте, добавление еще одной зависимости для такой простой задачи может быть излишним.
Эрих Кицмюллер

1
если он оказался медленным, вы также можете отправить собственное исправление для функции.
будет


13

Если вы хотите только пробелы, то как насчет:

String spaces = (n==0)?"":String.format("%"+n+"s", "");

что приведет к abs (n) пробелам;


6
Как это для скорости, хотя? У меня под впечатлением формат относительно медленный. Он должен проанализировать строку, прежде чем он сможет ее создать.
К. Росс

11

начиная с Java 11 :

" ".repeat(10);

начиная с Java 8 :

generate(() -> " ").limit(10).collect(joining());

где:

import static java.util.stream.Collectors.joining;
import static java.util.stream.Stream.generate;

9

Начиная с Java 11 вы можете просто использовать String.repeat(count)для решения вашей проблемы.

Возвращает строку, значением которой является конкатенация этой строки несколько countраз.

Если эта строка пуста или countравна нулю, возвращается пустая строка.

Таким образом, вместо цикла ваш код будет выглядеть так:

" ".repeat(length);

8

Я думаю, что это меньше кода, это возможно, он использует класс Guava Joiner:

Joiner .on (""). Join ( Collections.nCopies (10, ""));


Самая короткая строка кода, но добавление большой библиотеки только для этой простой задачи не имеет смысла. Я могу создать JAR-файл меньшего размера с помощью одного метода, pubic String n(int n) { ... }который позволит создавать еще «меньший» код: n(10)но, опять же, не имеет никакого смысла.
Исапир

@isapir Это отличный ответ для людей, которые уже используют
гуаву

1
@nasch Гуава не была частью вопроса. Но в любом случае, в 2018 году там действительно нет оснований использовать Столяр гуавы, когда вы можете использовать string.join () , которая была добавлена в Java 8. См stackoverflow.com/a/43011939/968244
isapir

@isapir Нет, Гуава был ответом. Ответы в StackOverflow имеют более широкую аудиторию, чем просто человек, задающий вопрос, поэтому хорошо, если некоторые ответы не являются лучшими для этого человека. И комментарий о String.join () великолепен, хотя я не уверен, что он доступен во всех версиях Android, который широко используется в Java.
nasch

7

Вы можете использовать стандартную String.formatфункцию для генерации N пробелов. Например:

String.format("%5c", ' ');

Делает строку с 5 пробелами.

или

int count = 15;
String fifteenSpacebars = String.format("%" + count + "c", ' ');

Делает строку из 15 пробелов.

Если вы хотите, чтобы другой символ повторялся, вы должны заменить пробелы на нужный символ:

int count = 7;
char mySymbol = '#';
System.out.println(String.format("%" + count + "c", ' ').replaceAll("\\ ", "\\" + mySymbol));

Вывод:

#######

6

Мой вклад основан на алгоритме быстрого возведения в степень.

/**
 * Repeats the given {@link String} n times.
 * 
 * @param str
 *            the {@link String} to repeat.
 * @param n
 *            the repetition count.
 * @throws IllegalArgumentException
 *             when the given repetition count is smaller than zero.
 * @return the given {@link String} repeated n times.
 */
public static String repeat(String str, int n) {
    if (n < 0)
        throw new IllegalArgumentException(
                "the given repetition count is smaller than zero!");
    else if (n == 0)
        return "";
    else if (n == 1)
        return str;
    else if (n % 2 == 0) {
        String s = repeat(str, n / 2);
        return s.concat(s);
    } else
        return str.concat(repeat(str, n - 1));
}

Я проверил алгоритм по двум другим подходам:

  • Обычный для цикла, используемого String.concat()для объединения строк
  • Обычный для цикла с использованием StringBuilder

Тестовый код (конкатенация с использованием цикла for и String.concat()замедление для большого n, поэтому я пропустил его после 5-й итерации).

/**
 * Test the string concatenation operation.
 * 
 * @param args
 */
public static void main(String[] args) {
    long startTime;
    String str = " ";

    int n = 1;
    for (int j = 0; j < 9; ++j) {
        n *= 10;
        System.out.format("Performing test with n=%d\n", n);

        startTime = System.currentTimeMillis();
        StringUtil.repeat(str, n);
        System.out
                .format("\tStringUtil.repeat() concatenation performed in    %d milliseconds\n",
                        System.currentTimeMillis() - startTime);

        if (j <5) {
            startTime = System.currentTimeMillis();
            String string = "";
            for (int i = 0; i < n; ++i)
                string = string.concat(str);
            System.out
                    .format("\tString.concat() concatenation performed in        %d milliseconds\n",
                            System.currentTimeMillis() - startTime);
        } else
            System.out
                    .format("\tString.concat() concatenation performed in        x milliseconds\n");
        startTime = System.currentTimeMillis();
        StringBuilder b = new StringBuilder();
        for (int i = 0; i < n; ++i)
            b.append(str);
        b.toString();
        System.out
                .format("\tStringBuilder.append() concatenation performed in %d milliseconds\n",
                        System.currentTimeMillis() - startTime);
    }
}

Полученные результаты:

Performing test with n=10
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        0 milliseconds
    StringBuilder.append() concatenation performed in 0 milliseconds
Performing test with n=100
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        1 milliseconds
    StringBuilder.append() concatenation performed in 0 milliseconds
Performing test with n=1000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        1 milliseconds
    StringBuilder.append() concatenation performed in 1 milliseconds
Performing test with n=10000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        43 milliseconds
    StringBuilder.append() concatenation performed in 5 milliseconds
Performing test with n=100000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        1579 milliseconds
    StringBuilder.append() concatenation performed in 1 milliseconds
Performing test with n=1000000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 10 milliseconds
Performing test with n=10000000
    StringUtil.repeat() concatenation performed in    7 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 112 milliseconds
Performing test with n=100000000
    StringUtil.repeat() concatenation performed in    80 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 1107 milliseconds
Performing test with n=1000000000
    StringUtil.repeat() concatenation performed in    1372 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 12125 milliseconds

Вывод:

  • Для больших n- используйте рекурсивный подход
  • Для маленьких n- петля имеет достаточную скорость

4
Это интересная реализация. К сожалению, вопреки вашим выводам, это очень неэффективно для больших русских. Я подозреваю, что это из-за большого количества выделений памяти, которые происходят каждый раз, когда вы объединяете строки. Попробуйте написать его как обертку вокруг рекурсивного метода, который принимает StringBuilder вместо String. Могу поспорить, вы найдете результаты будут намного лучше.
Klitos Kyriacou

3
Есть много мер предосторожности, которые вы должны предпринять, когда проводите микро-бенчмаркинг чего-то подобного, и я не думаю, что вы принимаете какие-либо из них! Доминирующие вопросы производительности вокруг этого кода легко могут быть связаны с тем, является ли он скомпилированным Hotspot, сколько мусора он создает и т. Д. Бьюсь об заклад, все эти ifутверждения могут испортить предсказание ветвления ЦП. Это должно быть действительно переделано с использованием JMH ( openjdk.java.net/projects/code-tools/jmh ), иначе это немного бессмысленно.
SusanW

Обратите внимание, что эта реализация имеет сложность O (n * log n), в то время как StringBuilder - O (n) :)
Лев Леонтьев


4

Учитывая, что мы имеем:

String c = "c"; // character to repeat, for empty it would be " ";
int n = 4; // number of times to repeat
String EMPTY_STRING = ""; // empty string (can be put in utility class)

Java 8 (используя Stream)

String resultOne = IntStream.range(0,n)
   .mapToObj(i->c).collect(Collectors.joining(EMPTY_STRING)); // cccc

Java 8 (Использование nCopies)

String resultTwo = String.join(EMPTY_STRING, Collections.nCopies(n, c)); //cccc

1
Collectors.joining(EMPTY_STRING)эквивалентноCollectors.joining()
PPartisan

2

У RandomStringUtils есть условие для создания строки из заданного размера ввода. Не могу прокомментировать скорость, но это один вкладыш.

RandomStringUtils.random(5,"\t");

создает вывод

\ Т \ т \ т \ т \ т

желательно, если вы не хотите видеть \ 0 в вашем коде.



1

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



0

Просто замените ваш StringBuffer на StringBuilder . Трудно победить это.

Если ваша длина - большое число, вы можете реализовать более эффективное (но более неуклюжее) самостоятельное добавление, дублируя длину в каждой итерации:

 public static String dummyString(char c, int len) {
  if( len < 1 ) return "";
  StringBuilder sb = new StringBuilder(len).append(c);
  int remnant = len - sb.length();
  while(remnant  > 0) {
   if( remnant  >= sb.length() ) sb.append(sb);
   else sb.append(sb.subSequence(0, remnant));
   remnant  = len - sb.length();
  }
  return sb.toString();
 }

Кроме того, вы можете попробовать Arrays.fill()подход ( ответ FrustratedWithFormsDesigner ).


Можете ли вы назвать некоторые переменные более чем одним символом?
К. Росс

0

Вы можете заменить StringBufferна StringBuilder(последний не синхронизирован, может быть быстрее в однопоточном приложении)

И вы можете создать StringBuilderэкземпляр один раз, вместо того, чтобы создавать его каждый раз, когда вам это нужно.

Что-то вроде этого:

class BuildString {
     private final StringBuilder builder = new StringBuilder();
     public String stringOf( char c , int times ) {

         for( int i = 0 ; i < times ; i++  ) {
             builder.append( c );
         }
         String result = builder.toString();
         builder.delete( 0 , builder.length() -1 );
         return result;
      }

  }

И используйте это так:

 BuildString createA = new BuildString();
 String empty = createA.stringOf( ' ', 10 );

Если вы храните вашу createAпеременную экземпляра, вы можете сэкономить время на создании экземпляров.

Это не является потокобезопасным, если у вас есть несколько потоков, каждый поток должен иметь свою собственную копию.


0

Для хорошей производительности объедините ответы от aznilamir и от FrustratedWithFormsDesigner

private static final String BLANKS = "                       ";
private static String getBlankLine( int length )
{
    if( length <= BLANKS.length() )
    {
        return BLANKS.substring( 0, length );
    }
    else
    {
        char[] array = new char[ length ];
        Arrays.fill( array, ' ' );
        return new String( array );
    }
}

Отрегулируйте размер в BLANKSзависимости от ваших требований. Моя конкретная BLANKSстрока имеет длину около 200 символов.


0

Есть такой метод. Это добавляет необходимые пробелы в конце данных, Stringчтобы сделать данные Stringдля длины определенной длины.

public static String fillSpaces (String str) {

    // the spaces string should contain spaces exceeding the max needed
    String spaces = "                                                   ";
    return str + spaces.substring(str.length());
}

0

Хотите, чтобы String был фиксированного размера, чтобы вы могли дополнять или усекать для табулирования данных ...

class Playground {
    private static String fixStrSize(String s, int n) {
        return String.format("%-" + n + "s", String.format("%." + n +"s", s));
    }

    public static void main(String[ ] args) {
        System.out.println("|"+fixStrSize("Hell",8)+"|");
        System.out.println("|"+fixStrSize("Hells Bells Java Smells",8)+"|");
    }
}

|Hell    |
|Hells Be|

Отличная ссылка здесь .


0

Это сработало для меня без использования каких-либо внешних библиотек в Java 8

String sampleText = "test"
int n = 3;
String output = String.join("", Collections.nCopies(n, sampleText));
System.out.println(output);

И вывод

testtesttest

0

int c = 10; Строковые пробелы = String.format ("%" + c + "c", ''); это решит вашу проблему.


-1

Простой метод, как показано ниже, также может быть использован

public static String padString(String str, int leng,char chr) {
        for (int i = str.length(); i <= leng; i++)
            str += chr;
        return str;
    }

Это значительно медленнее.
SLaks

Вы можете использовать StringBuffer вместо конкатенации строк, если строки, с которыми вы имеете дело, имеют большой размер. Это значительно повлияет на скорость
Яшпал Сингла

-2

как насчет этого?

public String fillSpaces(int len) {
    /* the spaces string should contain spaces exceeding the max needed */  
    String spaces = "                                                   ";
    return spaces.substring(0,len);
}

РЕДАКТИРОВАТЬ: я написал простой код для проверки концепции и вот что я нашел.

Способ 1: добавление одного пробела в цикле:

  public String execLoopSingleSpace(int len){
    StringBuilder sb = new StringBuilder();

    for(int i=0; i < len; i++) {
        sb.append(' ');
    }

    return sb.toString();
  }

Метод 2: добавить 100 пробелов и цикл, затем подстрока:

  public String execLoopHundredSpaces(int len){
    StringBuilder sb = new StringBuilder("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ");

    for (int i=0; i < len/100 ; i++) {
        sb.append("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ");
    }

    return sb.toString().substring(0,len);
  }

В результате я получаю 12,345,678 пробелов:

C:\docs\Projects> java FillSpace 12345678
method 1: append single spaces for 12345678 times. Time taken is **234ms**. Length of String is 12345678
method 2: append 100 spaces for 123456 times. Time taken is **141ms**. Length of String is 12345678
Process java exited with code 0

и на 10 000 000 мест:

C:\docs\Projects> java FillSpace 10000000
method 1: append single spaces for 10000000 times. Time taken is **157ms**. Length of String is 10000000
method 2: append 100 spaces for 100000 times. Time taken is **109ms**. Length of String is 10000000
Process java exited with code 0

Объединение прямого распределения и итерации всегда занимает меньше времени, в среднем на 60 мс при создании огромных пространств. Для меньших размеров оба результата незначительны.

Но, пожалуйста, продолжайте комментировать :-)


3
@ aznilamir: Хм, как вы будете делать для 10k пробелов?
Jayan

Идея состоит в том, чтобы объединить цикл и прямое выделение 100 пробелов. Вот фрагменты кода:
aznilamir

Почему вы используете несколько добавлений, чтобы добавить 100 символов? Почему бы не добавить 100 символов?
nycynik

-3

Я не знаю встроенного метода для того, о чем вы спрашиваете. Однако для небольшой фиксированной длины, например 10, ваш метод должен быть достаточно быстрым.


Если бы это была небольшая фиксированная длина, например 10.
С. Росс
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.