Створіть рядок з 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 замість StringBuffer

Додаючи на початку вихідний розмір буфера, легко обчислити полегшує справи в управлінні пам'яттю !. StringBuilder outputBuffer = новий StringBuilder (повторити * base.length ());
Віктор


Якщо ви хочете додати один пробіл, скористайтеся append(' ')натомість ... це передбачає трохи менше обчислень ...
Ерк

Відповіді:


36

Цикл for буде оптимізований компілятором. У таких випадках, як ваш, вам не потрібно піклуватися про оптимізацію самостійно. Довіряйте компілятору.

BTW, якщо є спосіб створити рядок з n символів пробілу, то він кодується так само, як і ви.


1
навіть якщо це не було оптимізовано - як часто це створюється у вашій програмі, - якщо часто зберігайте в статичній змінній чи іншому кеші
mmmmmm

btw Array.fill () просто перебирає масив. / мені потрібно, безумовно, більше балів, щоб прокоментувати інші пости :)
kalkin

@Марка довжини мінлива, тому здається нерозумним зберегти її. Що б я зробив, мав static Dictionary<int, String>?
C. Росс

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
Перевіривши джерело, здається, що це насправді робить саме те, що розміщено в ОП: рядок 806 docjar.com/html/api/java/util/Arrays.java.html
Pops

2
@Lord Torgamus Чи було відредаговано питання ОП? Тому що у версії, яку я бачу, він лупить на StringBuffer.append (), а версія Frustrated виконує заливку (що, звичайно, циклічно виконує завдання для масиву для масиву). Зовсім не те саме.
CPerkins

@CPerkins, досить чесно, мені не було зрозуміло. Моя думка полягає в тому, що вони обидва роблять введення символів за символом всередині циклу for.
Вискокує

1
@Lord Torgamus - Погодився з цим. Шкода, що jvm не може просто створити мемсет. Ціна, яку ми платимо за широкі персонажі.
CPerkins

2
@Pops, варто відзначити, що якщо ця Arrays.fill()функція послідовно використовується для виконання цієї роботи у вашому коді та (стандартному та зовнішньому) коді бібліотеки, тоді вона є хорошим кандидатом для компіляції Hotspot і має більший шанс виявити ваш кеш процесора. Майбутні JVM можуть призначити його як внутрішню функцію.
Розігрування власних рук позбавляє

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", "");

що призведе до абс (n) пробілів;


6
Як це на швидкість? Я під форматом вражень відносно повільний. Він повинен проаналізувати рядок, перш ніж він зможе його створити.
C. Росс

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 Це відмінний відповідь для людей , які вже використовують Guava
нащ

1
@nasch Guava не був частиною питання. Але в будь-якому випадку, в 2018 році там дійсно немає підстав використовувати Столяр гуави, коли ви можете використовувати string.join () , яка була додана в Java 8. Див stackoverflow.com/a/43011939/968244
isapir

@isapir Ні, Гуава була відповіддю. Відповіді на StackOverflow мають широку аудиторію, ніж просто людина, яка задає питання, тому добре, якщо деякі відповіді не найкращі для того, хто запитує. І коментар про String.join () - чудовий, хоча я не впевнений, чи доступний він у всіх версіях Android, що є видатним використанням Java.
нащ

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()для об'єднання рядків
  • Регулярно для циклу, використовуючи a 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. Б'юсь об заклад, ви знайдете, що результати будуть набагато кращими.
Клітос Кіріако

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 (використання потоку)

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");

створює вихід

\ t \ t \ t \ t \ t

бажано, якщо ви не хочете бачити \ 0 у своєму коді.



1

У більшості випадків вам потрібні лише рядки до довжини стежок, скажімо, на 100 пробілів. Ви можете підготувати масив Strings, у якому число індексу дорівнює розміру заповненої пробілом рядка, і шукати рядок, якщо потрібна довжина знаходиться в межах, або створити її на вимогу, якщо вона знаходиться за межами межі.



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 ).


Чи можете ви назвати деякі змінні більш ніж одним символом?
C. Росс

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

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

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


-1

Простий метод, як показано нижче, також може бути використаний

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

Це субаціально повільніше.
СЛАкс

Ви можете використовувати StringBuffer замість з'єднання рядків, якщо рядки, з якими ви маєте справу, мають великі розміри. Це значно вплине на швидкість
Yashpal Singla

-2

як щодо цього?

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

EDIT: Я написав простий код для тестування концепції, і ось що я знайшов.

Спосіб 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: Гм, як ви будете робити 10 пробілів?
Джаян

Ідея полягає у поєднанні циклу та прямого виділення 100 пробілів. Ось фрагменти коду:
aznilamir

Чому для додавання 100 символів використовується декілька додатків? Чому не додається 100 знаків?
nycynik

-3

Я не знаю жодного вбудованого методу для того, про що ви питаєте. Однак для невеликої фіксованої довжини, як 10, ваш метод повинен бути досить швидким.


Якби це була лише невелика фіксована довжина, як 10.
С. Росс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.