Простий спосіб повторити струну в Java


597

Я шукаю простий метод commons або оператор, який дозволяє мені повторити кілька рядків n разів. Я знаю, що можу написати це за допомогою циклу for, але я хочу уникати циклів, коли це необхідно, і десь повинен існувати простий прямий метод.

String str = "abc";
String repeated = str.repeat(3);

repeated.equals("abcabcabc");

Пов’язані з:

повторити рядок javascript Створити NSString, повторивши інший рядок задану кількість разів

Відредаговано

Я намагаюся уникати циклів, коли вони зовсім не потрібні, оскільки:

  1. Вони додають до кількості рядків коду, навіть якщо вони заховані в іншій функції.

  2. Хтось, хто читає мій код, повинен зрозуміти, що я роблю в цьому для циклу. Навіть якщо він коментований і має змістовні назви змінних, вони все одно повинні переконатися, що він нічого не робить "розумним".

  3. Програмісти люблять укладати розумні речі для циклів, навіть якщо я пишу це "робити лише те, що призначено робити", що не виключає, щоб хтось прийшов і додав додаткові розумні "виправлення".

  4. Вони дуже часто легко помиляються. Для циклів, що включають індекси, як правило, генеруються одні помилки.

  5. Для циклів часто повторно використовують одні і ті ж змінні, збільшуючи шанс дійсно важко знайти шкали помилок.

  6. Для петель збільшуйте кількість місць, які повинен шукати мисливець на помилок.


35
Я розумію, що циклі можуть спричинити деякі реальні проблеми. Але не варто намагатися уникати циклів "будь-якою ціною", тому що якщо це коштує для вас читабельності, ремонтопридатності та швидкості, ви будете контрпродуктивними. Це один із таких випадків.
— Imagist

9
"Вони додають до кількості рядків коду, навіть якщо вони заховані в іншій функції" ... вау, просто уау. Big-O, не LoC
— Піролістичний

7
@imagist Я уникаю циклів у ситуаціях, коли це коштує мені читабельності та ремонтопридатності. Я вважаю швидкість як найменш важливу проблему тут (насправді непроблема). Я думаю, що для циклів надмірно використовуються, і я намагаюся навчитися використовувати для циклів лише тоді, коли вони необхідні, а не як рішення за замовчуванням.
— Етан Хайльман

3
@Pyrolistic Я не претендую на ефективність чи асимптотику. Швидше кажучи, що, записуючи менше коду та використовуючи бібліотечні функції, а не винаходити колесо, я зменшую площу поверхні помилок (Lines of Code), збільшуючи при цьому читабельність. Обидва хороших речі, я впевнений, що ви погодиться.
— Етан Хайльман

4
@ e5; вибачте за публікацію років потому. Я вважаю це питання настільки доречним. Якщо вставлено в метод, аргументи повинні бути протестовані (раз> = 0), викинуті помилки і т. Д. Це додає надійності, але і рядків коду для читання. Повторення рядка - це щось однозначне. Хто читає код, точно знає, що робить string.repeat, навіть без рядка коментаря чи javadoc. Якщо ми використовуємо стабільну бібліотеку, розумно думати, що настільки проста функція не має помилок, І все-таки вводиться певна форма перевірки "надійності", про яку нам навіть потрібно турбуватися. Якби я міг попросити 10 вдосконалень, цей (такий) спосіб був би одним.
— AgostinoX

Відповіді:


229

String::repeat

". ".repeat( 7 )  // Seven period-with-space pairs: . . . . . . . 

Нове в Java 11 - це метод, String::repeatякий виконує саме те, що ви просили:

String str = "abc";
String repeated = str.repeat(3);
repeated.equals("abcabcabc");

Його Javadoc говорить:

/**
 * Returns a string whose value is the concatenation of this
 * string repeated {@code count} times.
 * <p>
 * If this string is empty or count is zero then the empty
 * string is returned.
 *
 * @param count number of times to repeat
 *
 * @return A string composed of this string repeated
 * {@code count} times or the empty string if this
 * string is empty or count is zero
 *
 * @throws IllegalArgumentException if the {@code count} is
 * negative.
 *
 * @since 11
 */ 

2
@Nicolai вихідний код для нього, про всяк випадок, коли хтось піклується hg.openjdk.java.net/jdk/jdk/file/fc16b5f193c7/src/java.base/…
— Євген

7
я навіть не бачив Java - ще на вулиці (і не буде в протягом тривалого часу ..) - і 11 , по- видимому встановлений на корабель ..
— javadba

1
Ймовірно , очевидно, але ви можете викликати цей метод строкового литерала теж:"abc".repeat(3)
— Boann

895

Ось найкоротша версія (потрібна Java 1.5+):

repeated = new String(new char[n]).replace("\0", s);

Де nкількість разів, яку ви хочете повторити рядок, і чи sє рядок для повторення.

Ні імпорт, ні бібліотеки не потрібні.


22
Я не думаю, що це взагалі заплутано. Примітивні типи ( char[]в даному випадку) інстанціюються з нулями, тоді а Stringстворюється з char[], а нульові replaced()знаки - з потрібним символомs
— Amarok

32
Хоча це дуже розумно (+1), я думаю, це в значній мірі доводить те, що для циклів часто роблять чіткіший код
— Річард Тінгл

75
Для тих, хто скаржиться на затуманення, читабельність залежить від грамотності. Це цілком зрозуміло в тому, що він робить, і навчальний для тих, хто може цього не бачити відразу. Це, до речі, ви отримуєте з Java.
— дансальмо

11
Для кращої продуктивності ...replace('\0', str)слід використовувати замість версії String.
— user686249

11
@ user686249: Є лише replace(char oldChar, char newChar)і replace(CharSequence target, CharSequence replacement)тому я не бачу, як це могло б працювати
— user102008

334

Якщо ви використовуєте Java <= 7 , це настільки ж стисло, як і виходить:

// create a string made up of n copies of string s
String.format("%0" + n + "d", 0).replace("0", s);

У Java 8 і вище існує більш читаний спосіб:

// create a string made up of n copies of string s
String.join("", Collections.nCopies(n, s));

Нарешті, для Java 11 і вище існує новий repeat​(int count)метод спеціально для цього ( посилання )

"abc".repeat(12);

Крім того, якщо ваш проект використовує бібліотеки java, є більше варіантів.

Для Apache Commons :

StringUtils.repeat("abc", 12);

Для Google Guava :

Strings.repeat("abc", 12);

4
Перший викликає виняток, коли nдорівнює нулю.
— saka1029

Приклад java 8 не компілюється -> Невідповідність типу: не можна перетворити зі списку <Character> у CharSequence
— Arigion

6
@Arigion sмає бути струнним, а неChar
— Caner

@Caner Дякую Моє погано, вибачаюся. Мабуть, я вчора був занадто стомлений. Вибачте за заборону. Я видаляю голосову заявку, як тільки зможу (блокується, поки питання не буде відредаговано)
— Арігіон

@Arigion жодних проблем не дав зрозуміти, що s - рядок
— Caner

312

Commons Lang StringUtils.repeat ()

Використання:

String str = "abc";
String repeated = StringUtils.repeat(str, 3);

repeated.equals("abcabcabc");

99
використання залежностей від одного методу для простоти в довгостроковій перспективі може призвести до бурхливого пекла
— dfa

81
Звичайно, за винятком того, що це загальна мова Я не думаю, що я ніколи не бачив проекту понад 5000 LOCS, на якому не було спільноти.
— Етан Хайльман

11
Commons Lang є відкритим кодом - завантажте його і подивіться. Звичайно, вона має петлю всередині, але це не так просто. Багато зусиль пішло на профілювання та оптимізацію цієї реалізації.
— ChssPly76

28
Я не уникаю циклів з причини ефективності (читайте мої причини у запитанні). Коли хтось бачить StringUtils.repeat, вони знають, що я роблю. Їм не потрібно хвилюватися, що я спробував написати власну версію повтору і допустив помилку. Це атомна когнітивна одиниця!
— Етан Хайльман

7
@ Thorbjørn Ravn Andersen - це може отримати набагато цікавіше, якщо все
— триватиметься

140

Java 8 String.joinпропонує чіткий спосіб зробити це разом із Collections.nCopies:

// say hello 100 times
System.out.println(String.join("", Collections.nCopies(100, "hello")));

7
Дякую тобі! Для android TextUtils.join () можна використовувати замість String.join ()
— MinaHany

2
Дякую за цю відповідь. Це, мабуть, найчистіший спосіб без використання жодної зовнішньої утиліти API oder! дуже добре!!
— Андреас М. Оберхайм

2
Приємним у цьому методі є те, що за допомогою приєднання ви можете надати символу роздільника, який виявиться дуже зручним, якщо ви, скажімо, створюєте список CSV. З усіма іншими методами у вас є характер припинення з'єднання, який потрібно позбавити в окремій операції.
— DroidOS

102

Ось спосіб зробити це за допомогою лише стандартних функцій String і без явних циклів:

// create a string made up of  n  copies of  s
repeated = String.format(String.format("%%%ds", n), " ").replace(" ",s);

5
Дивовижно :-) Хоч остерігайся, що п не стане нулем…!
— Ян Мейєр

4
@Vijay Dev & fortran: Ні, він мав на увазі replace(). У Java 1.5+ є перевантажена версія, replace()яка займає два CharSequenceс (включаючи Strings): download.oracle.com/javase/1.5.0/docs/api/java/lang/…
— user102008

79
Ой. Це некрасиво.
— Карел Білек

8
Скажімо, @mzuba n=3: спочатку форматується рядок, щоб виглядати щось на зразок %03d( %%це уникнути знаку відсотка), який є кодом форматування, щоб додати 3 нульових прокладки, потім формати 0з цим, що веде до 000, і, нарешті, замінює кожен 0рядками
— fortran

15
Ви можете зробити рішення менш потворним і простішим для розуміння: String.format ("% 0" + n + "d", 0) .replace ("0", s)
— Артур

87

Якщо ви схожі на мене і хочете користуватися Google Guava, а не Apache Commons. Ви можете використовувати метод повтору в класі Guava Strings.

Strings.repeat("-", 60);

2
... і отримати 3 Мб нових залежностей.
— MonoTrereed

6
@MonoThreaded Я подумав, що це піде само собою, але не включайте гуаву просто для того, щоб повторити рядок. Моя відповідь стосувалася того, якщо ви все одно користуєтеся гуавою, то це так, як ви це зробите.
— Джек

53

З , ви також можете використовувати Stream.generate.

import static java.util.stream.Collectors.joining;
...
String repeated = Stream.generate(() -> "abc").limit(3).collect(joining()); //"abcabcabc"

і ви можете обернути його простим корисним методом, якщо потрібно:

public static String repeat(String str, int times) {
   return Stream.generate(() -> str).limit(times).collect(joining());
}

6
... або return IntStream.range(0, times).mapToObj(i -> str).collect(joining());яка паралелізація краще
— Алексіс К.

32

Отже, ви хочете уникати циклів?

Ось у вас це:

public static String repeat(String s, int times) {
    if (times <= 0) return "";
    else return s + repeat(s, times-1);
}

(звичайно, я знаю, що це некрасиво і неефективно, але в ньому немає циклів :-p)

Ви хочете, щоб це було простіше і красивіше? використовувати jython:

s * 3

Редагувати : давайте трохи оптимізуємо це :-D

public static String repeat(String s, int times) {
   if (times <= 0) return "";
   else if (times % 2 == 0) return repeat(s+s, times/2);
   else return s + repeat(s+s, times/2);
}

Edit2 : Я зробив швидкий і брудний орієнтир для 4 основних альтернатив, але я не встигаю запустити його кілька разів, щоб отримати засоби та побудувати графік для кількох входів ... Тож ось код, якщо хто хоче спробувати:

public class Repeat {
    public static void main(String[] args)  {
        int n = Integer.parseInt(args[0]);
        String s = args[1];
        int l = s.length();
        long start, end;

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatLog2(s,i).length()!=i*l) throw new RuntimeException();
        }
        end = System.currentTimeMillis();
        System.out.println("RecLog2Concat: " + (end-start) + "ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatR(s,i).length()!=i*l) throw new RuntimeException();
        }               
        end = System.currentTimeMillis();
        System.out.println("RecLinConcat: " + (end-start) + "ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatIc(s,i).length()!=i*l) throw new RuntimeException();
        }
        end = System.currentTimeMillis();
        System.out.println("IterConcat: " + (end-start) + "ms");

        start = System.currentTimeMillis();
        for (int i = 0; i < n; i++) {
            if(repeatSb(s,i).length()!=i*l) throw new RuntimeException();
        }
        end = System.currentTimeMillis();
        System.out.println("IterStrB: " + (end-start) + "ms");
    }

    public static String repeatLog2(String s, int times) {
        if (times <= 0) {
            return "";
        }
        else if (times % 2 == 0) {
            return repeatLog2(s+s, times/2);
        }
        else {
           return s + repeatLog2(s+s, times/2);
        }
    }

    public static String repeatR(String s, int times) {
        if (times <= 0) {
            return "";
        }
        else {
            return s + repeatR(s, times-1);
        }
    }

    public static String repeatIc(String s, int times) {
        String tmp = "";
        for (int i = 0; i < times; i++) {
            tmp += s;
        }
        return tmp;
    }

    public static String repeatSb(String s, int n) {
        final StringBuilder sb = new StringBuilder();
        for(int i = 0; i < n; i++) {
            sb.append(s);
        }
        return sb.toString();
    }
}

Це потребує 2 аргументів, перший - кількість ітерацій (кожна функція виконується з аргументами повторів arg від 1..n), а друга - рядок для повторення.

Поки швидкий огляд часу, що працює з різними вхідними даними, залишає такий рейтинг (краще до гіршого):

  1. Ітераційне додавання StringBuilder (1x).
  2. Рекурсивні виклики log2 виклику log2 (~ 3x).
  3. Рекурсивні лінійні виклики конкатенації (~ 30x).
  4. Ітеративна конкатенація лінійна (~ 45x).

Я ніколи не здогадувався, що рекурсивна функція швидша за forцикл: -o

Повеселіться (ctional xD).


1
+1 для рекурсії і, очевидно, є хакером. Я також не думаю, що це настільки неефективно, конкатенація рядків - це не той воєнний злочин, який колись був, тому що + насправді є лише UTH stringBuilder. Дивіться stackoverflow.com/questions/47605/java-string-concatenation та schneide.wordpress.com/2009/02/23/… . Цікаво, скільки всіх цих стеків штовхає і спливає від рекурсійних витрат, або якщо гаряча точка піклується про них. Дуже хотілося б, щоб у мене був вільний час для його визначення. Можливо, хтось ще?
— Етан Хайльман

@ e5: fortran має рацію; це рішення можна зробити більш ефективним. Ця реалізація зайво створить новий StringBuilder (і новий String) для кожної рекурсії. Все-таки приємне рішення, хоча.
— пограбувати

3
@ e5 Я б хотів, щоб я був хакером Lisp xD ... Якби я був, я використав би рекурсивну функцію хвоста :-p
— fortran

1
Мікро-орієнтири не дуже добре працюють на Java. Намагатись виміряти швидкість таких реалізацій, як це не добре.
— ceklock

@tecnotron Я знаю, але все-таки вони кращі, ніж нічого ... І єдиним "сюрпризом" була невелика різниця між злиттям наївного циклу і лінійною рекурсією.
— фортран

20

Це містить менше символів, ніж ваше запитання

public static String repeat(String s, int n) {
    if(s == null) {
        return null;
    }
    final StringBuilder sb = new StringBuilder(s.length() * n);
    for(int i = 0; i < n; i++) {
        sb.append(s);
    }
    return sb.toString();
}

4
Він містить більше символів, ніж моя відповідь StringUtils.repeat (str, n).
— Етан Хайльман

8
Якщо ви вже не використовуєте Apache Commons, ця відповідь набагато менше клопоту - не завантажуйте іншу бібліотеку, включаючи її на свій класний шлях, переконайтесь, що її ліцензія сумісна з вашою тощо.
— Пол Томблін,

7
Будь ласка, ніколи не повертайте null - у такому випадку повертайте порожній рядок, що дозволяє вам завжди використовувати повернене значення без відмітки. Інакше те, що я рекомендував би використовувати плакат.
— Thorbjørn Ravn Andersen

7
Ну, є три способи впоратися, якщо s недійсний. 1. Передайте помилку (return null), 2. Прихойте помилку (return ""), 3. Киньте NPE. Сховати помилку та кинути NPE - це не круто, тому я передав помилку.
— Піролістичний

1
@EthanHeilman додай вартості 2 Мб, commons-lang3.3.1-sourcesі ти вже не такий хороший;) Але якщо хтось уже має commons-lang, я підтримую твою відповідь.
— TWiStErRob

9

на основі відповіді fortran, це рекусивна версія, яка використовує StringBuilder:

public static void repeat(StringBuilder stringBuilder, String s, int times) {
    if (times > 0) {
        repeat(stringBuilder.append(s), s, times - 1);
    }
}

public static String repeat(String s, int times) {
    StringBuilder stringBuilder = new StringBuilder(s.length() * times);
    repeat(stringBuilder, s, times);
    return stringBuilder.toString();
}

1
циклічне циклічне замість рекурсії зменшить кількість кадрів стека для великої кількості повторів.
— La-comadreja

7

Використовувати долар просто, як набрати:

@Test
public void repeatString() {
    String string = "abc";
    assertThat($(string).repeat(3).toString(), is("abcabcabc"));
}

PS: повторення робіт також для масиву, списку, встановлення тощо


3
чи дійсно потрібен метод assertThat ()?
— ceklock

7

Я хотів функцію створити список знаків запитань, розділених комами для цілей JDBC, і знайшов цю публікацію. Отже, я вирішив взяти два варіанти і побачити, який з них краще. Після 1 мільйона ітерацій садовий сорт StringBuilder зайняв 2 секунди (fun1), а криптовалюта нібито більш оптимальної версії (fun2) зайняла 30 секунд. Який сенс знову бути дурним?

private static String fun1(int size) {
    StringBuilder sb = new StringBuilder(size * 2);
    for (int i = 0; i < size; i++) {
        sb.append(",?");
    }
    return sb.substring(1);
}

private static String fun2(int size) {
    return new String(new char[size]).replaceAll("\0", ",?").substring(1);
}

3
Я маю сенс, що другий зайняв би набагато більше часу. Він виконує пошук рядків, а потім змінює символ рядка за символом.
— Етан Хайльман

7

Рішення OOP

Майже в кожній відповіді пропонується статична функція як рішення, але, обмірковуючи об'єктно-орієнтовану (для цілей повторного використання та ясності), я придумав рішення за допомогою Делегації через інтерфейс CharSequence-Interface (який також відкриває зручність використання змінних класів CharSequence-Classes).

Наступний клас може використовуватися з або без Separator-String / CharSequence або кожен виклик до "toString ()" будує остаточний повторний рядок. Вхід / роздільник не обмежується лише класом String, але це може бути кожен клас, який реалізує CharSequence (наприклад, StringBuilder, StringBuffer тощо)!

Джерело-код:

/**
 * Helper-Class for Repeating Strings and other CharSequence-Implementations
 * @author Maciej Schuttkowski
 */
public class RepeatingCharSequence implements CharSequence {
    final int count;
    CharSequence internalCharSeq = "";
    CharSequence separator = "";
    /**
     * CONSTRUCTOR - RepeatingCharSequence
     * @param input CharSequence to repeat
     * @param count Repeat-Count
     */
    public RepeatingCharSequence(CharSequence input, int count) {
        if(count < 0)
            throw new IllegalArgumentException("Can not repeat String \""+input+"\" less than 0 times! count="+count);
        if(count > 0)
            internalCharSeq = input;
        this.count = count;
    }
    /**
     * CONSTRUCTOR - Strings.RepeatingCharSequence
     * @param input CharSequence to repeat
     * @param count Repeat-Count
     * @param separator Separator-Sequence to use
     */
    public RepeatingCharSequence(CharSequence input, int count, CharSequence separator) {
        this(input, count);
        this.separator = separator;
    }

    @Override
    public CharSequence subSequence(int start, int end) {
        checkBounds(start);
        checkBounds(end);
        int subLen = end - start;
        if (subLen < 0) {
            throw new IndexOutOfBoundsException("Illegal subSequence-Length: "+subLen);
        }
        return (start == 0 && end == length()) ? this
                    : toString().substring(start, subLen);
    }
    @Override
    public int length() {
        //We return the total length of our CharSequences with the separator 1 time less than amount of repeats:
        return count < 1 ? 0
                : ( (internalCharSeq.length()*count) + (separator.length()*(count-1)));
    }
    @Override
    public char charAt(int index) {
        final int internalIndex = internalIndex(index);
        //Delegate to Separator-CharSequence or Input-CharSequence depending on internal index:
        if(internalIndex > internalCharSeq.length()-1) {
            return separator.charAt(internalIndex-internalCharSeq.length());
        }
        return internalCharSeq.charAt(internalIndex);
    }
    @Override
    public String toString() {
        return count < 1 ? ""
                : new StringBuilder(this).toString();
    }

    private void checkBounds(int index) {
        if(index < 0 || index >= length())
            throw new IndexOutOfBoundsException("Index out of Bounds: "+index);
    }
    private int internalIndex(int index) {
        // We need to add 1 Separator-Length to total length before dividing,
        // as we subtracted one Separator-Length in "length()"
        return index % ((length()+separator.length())/count);
    }
}

Приклад використання:

public static void main(String[] args) {
    //String input = "12345";
    //StringBuffer input = new StringBuffer("12345");
    StringBuilder input = new StringBuilder("123");
    //String separator = "<=>";
    StringBuilder separator = new StringBuilder("<=");//.append('>');
    int repeatCount = 2;

    CharSequence repSeq = new RepeatingCharSequence(input, repeatCount, separator);
    String repStr = repSeq.toString();

    System.out.println("Repeat="+repeatCount+"\tSeparator="+separator+"\tInput="+input+"\tLength="+input.length());
    System.out.println("CharSeq:\tLength="+repSeq.length()+"\tVal="+repSeq);
    System.out.println("String :\tLength="+repStr.length()+"\tVal="+repStr);

    //Here comes the Magic with a StringBuilder as Input, as you can append to the String-Builder
    //and at the same Time your Repeating-Sequence's toString()-Method returns the updated String :)
    input.append("ff");
    System.out.println(repSeq);
    //Same can be done with the Separator:
    separator.append("===").append('>');
    System.out.println(repSeq);
}

Приклад-вихід:

Repeat=2    Separator=<=    Input=123   Length=3
CharSeq:    Length=8    Val=123<=123
String :    Length=8    Val=123<=123
123ff<=123ff
123ff<====>123ff

4
рідко я бачив речі, які огидні: /
— Вересень

6

використовуючи лише класи JRE ( System.arraycopy ) і намагаючись мінімізувати кількість тимчасових об'єктів, ви можете написати щось на зразок:

public static String repeat(String toRepeat, int times) {
    if (toRepeat == null) {
        toRepeat = "";
    }

    if (times < 0) {
        times = 0;
    }

    final int length = toRepeat.length();
    final int total = length * times;
    final char[] src = toRepeat.toCharArray();
    char[] dst = new char[total];

    for (int i = 0; i < total; i += length) {
        System.arraycopy(src, 0, dst, i, length);
    }

    return String.copyValueOf(dst);
}

EDIT

і без циклів ви можете спробувати:

public static String repeat2(String toRepeat, int times) {
    if (toRepeat == null) {
        toRepeat = "";
    }

    if (times < 0) {
        times = 0;
    }

    String[] copies = new String[times];
    Arrays.fill(copies, toRepeat);
    return Arrays.toString(copies).
              replace("[", "").
              replace("]", "").
              replaceAll(", ", "");
}

EDIT 2

використання колекцій ще коротше:

public static String repeat3(String toRepeat, int times) {
    return Collections.nCopies(times, toRepeat).
           toString().
           replace("[", "").
           replace("]", "").
           replaceAll(", ", "");
}

однак мені все ж подобається перша версія.


6
-1: занадто спритний наполовину. Якщо ваша мета зробити ваш код читабельним чи ефективним, ці "рішення" не є хорошою ідеєю. 'повтор' може бути просто переписаний за допомогою StringBuilder (встановлення початкової ємності). І 'повтор2' / 'повторити3' дійсно неефективні, і залежать від не визначеного синтаксису рядка, виробленого String []. ToString ().
— Стівен C

@Thorb: абсолютно з цим кодом ви не можете використовувати "metacharacter", [],
— dfa

@Stephen: питання було відредаговано, щоб явно не вимагати циклів. Відповідь на основі StringBuilder вже була надана, тому я уникав публікувати дублікат
— dfa

@Stephan: Я не можу розібратися в суті. Моя відредагована відповідь не містить циклу, як вимагається. Про ефективність запитів немає. Я думаю, що це питання є лише інтелектуальним зусиллям для створення конкатенації без циклу.
— dfa

@Stephan: Рядок, створений за допомогою Collection.toString (і Arrays.toString), чітко визначений у AbstractCollection.toString: "Рядок представлення складається із списку елементів колекції у порядку, який вони повертаються його ітератором, укладеним у квадратні дужки ( "[]"). Суміжні елементи розділені символами "," (кома та пробіл). "
— dfa

6

Не найкоротший, але (я думаю) найшвидший спосіб - це використовувати StringBuilder:

 /**
   * Repeat a String as many times you need.
   *
   * @param i - Number of Repeating the String.
   * @param s - The String wich you want repeated.
   * @return The string n - times.
   */
  public static String repeate(int i, String s) {
    StringBuilder sb = new StringBuilder();
    for (int j = 0; j < i; j++)
      sb.append(s);
    return sb.toString();
  }

5

Якщо швидкість викликає занепокоєння, то вам слід використовувати якомога менше копіювання пам'яті. Таким чином, потрібно працювати з масивами символів.

public static String repeatString(String what, int howmany) {
    char[] pattern = what.toCharArray();
    char[] res = new char[howmany * pattern.length];
    int length = pattern.length;
    for (int i = 0; i < howmany; i++)
        System.arraycopy(pattern, 0, res, i * length, length);
    return new String(res);
}

Для перевірки швидкості подібний оптимальний метод за допомогою StirngBuilder виглядає таким чином:

public static String repeatStringSB(String what, int howmany) {
    StringBuilder out = new StringBuilder(what.length() * howmany);
    for (int i = 0; i < howmany; i++)
        out.append(what);
    return out.toString();
}

і код для перевірки:

public static void main(String... args) {
    String res;
    long time;

    for (int j = 0; j < 1000; j++) {
        res = repeatString("123", 100000);
        res = repeatStringSB("123", 100000);
    }

    time = System.nanoTime();
    res = repeatString("123", 1000000);
    time = System.nanoTime() - time;
    System.out.println("elapsed repeatString: " + time);

    time = System.nanoTime();
    res = repeatStringSB("123", 1000000);
    time = System.nanoTime() - time;
    System.out.println("elapsed repeatStringSB: " + time);

}

І ось результати запуску з моєї системи:

elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937

Зауважте, що тест на цикл - це удар у JIT та отримання оптимальних результатів.


4

заради читабельності та портативності:

public String repeat(String str, int count){
    if(count <= 0) {return "";}
    return new String(new char[count]).replace("\0", str);
}

3

Якщо ви турбуєтесь про продуктивність, просто використовуйте StringBuilder всередині циклу та виконайте .toString () на виході з циклу. Чорт забирай, напиши свій власний клас Util і повторно використовуй його. 5 Рядок коду макс.


2

Мені дуже подобається це питання. Знань та стилів дуже багато. Тож я не можу залишити його без показу свого рок-н-ролу;)

{
    String string = repeat("1234567890", 4);
    System.out.println(string);
    System.out.println("=======");
    repeatWithoutCopySample(string, 100000);
    System.out.println(string);// This take time, try it without printing
    System.out.println(string.length());
}

/**
 * The core of the task.
 */
@SuppressWarnings("AssignmentToMethodParameter")
public static char[] repeat(char[] sample, int times) {
    char[] r = new char[sample.length * times];
    while (--times > -1) {
        System.arraycopy(sample, 0, r, times * sample.length, sample.length);
    }
    return r;
}

/**
 * Java classic style.
 */
public static String repeat(String sample, int times) {
    return new String(repeat(sample.toCharArray(), times));
}

/**
 * Java extreme memory style.
 */
@SuppressWarnings("UseSpecificCatch")
public static void repeatWithoutCopySample(String sample, int times) {
    try {
        Field valueStringField = String.class.getDeclaredField("value");
        valueStringField.setAccessible(true);
        valueStringField.set(sample, repeat((char[]) valueStringField.get(sample), times));
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

Вам це подобається?


1
У моєму більш екстремальному тесті я створюю 1 700 000 000 (1,7 гіга) довжини рядка, використовуючи -Xms4937m
— Даніель Де Леон

2
public static String repeat(String str, int times) {
    int length = str.length();
    int size = length * times;
    char[] c = new char[size];
    for (int i = 0; i < size; i++) {
        c[i] = str.charAt(i % length);
    }
    return new String(c);
}

2

Проста петля

public static String repeat(String string, int times) {
    StringBuilder out = new StringBuilder();
    while (times-- > 0) {
        out.append(string);
    }
    return out.toString();
}

2
перейти timesдо конструктора StringBuilder.
— Беруз.М

2

Спробуйте це:

public static char[] myABCs = {'a', 'b', 'c'};
public static int numInput;
static Scanner in = new Scanner(System.in);

public static void main(String[] args) {
    System.out.print("Enter Number of Times to repeat: ");
    numInput = in.nextInt();
    repeatArray(numInput);
}

public static int repeatArray(int y) {
    for (int a = 0; a < y; a++) {
        for (int b = 0; b < myABCs.length; b++) {
            System.out.print(myABCs[b]);                
        }
        System.out.print(" ");
    }
    return y;
}

2

Використовуючи рекурсію, ви можете зробити наступне (використовуючи потрійні оператори, максимум один рядок):

public static final String repeat(String string, long number) {
    return number == 1 ? string : (number % 2 == 0 ? repeat(string + string, number / 2) : string + repeat(string + string, (number - 1) / 2));
}

Я знаю, це некрасиво і, мабуть, неефективно, але це одна лінія!


Такий підхід я б застосував, але навіщо робити більше перевірок, ніж потрібно? номер повернення> 0? рядок + повтор (рядок, число-1): "";
— Fering

О, схоже, niczm25 відповів на це нижче
— Fering

@ Основна причина, щоб цей спосіб був середнім O (log N), а не O (N) завжди. Трохи більша оптимізація, ніж інша, хоча все ж погана.
— HyperNeutrino

2

прямолінійне однолінійне рішення:
потрібна Java 8

Collections.nCopies( 3, "abc" ).stream().collect( Collectors.joining() );

1

Незважаючи на ваше бажання не використовувати петлі, я думаю, ви повинні використовувати цикл.

String repeatString(String s, int repetitions)
{
    if(repetitions < 0) throw SomeException();

    else if(s == null) return null;

    StringBuilder stringBuilder = new StringBuilder(s.length() * repetitions);

    for(int i = 0; i < repetitions; i++)
        stringBuilder.append(s);

    return stringBuilder.toString();
}

Ваші причини невикористання циклу не є гарними. У відповідь на вашу критику:

  1. Яке б рішення ви не використовували, майже напевно буде довше цього. Використовуючи заздалегідь вбудовану функцію, підкладайте її лише до інших обкладинок.
  2. Хтось, хто читає ваш код, повинен з'ясувати, що ви робите в тій, що не має циклу. Враховуючи, що фор-цикл - це ідіоматичний спосіб зробити це, було б набагато простіше зрозуміти, якби ви зробили це з циклом for.
  3. Так хто - то може додати що - щось розумне, але, уникаючи для циклу ви будете робити що - то розумне . Це як умисно стріляти собі в ногу, щоб уникнути випадкової стрілянини в ногу.
  4. Помилки один за одним також розумно легко зрозуміти за допомогою одного тесту. Зважаючи на те, що вам слід тестувати свій код, помилку за одним бажанням слід легко виправити та зрозуміти. І варто зауважити: наведений вище код не містить помилки за одним. Для петель однаково легко підійти правильно.
  5. Тому не використовуйте повторно змінні. Це не винна ціль.
  6. Знову ж таки, будь-яке рішення, яке ви використовуєте. І як я зазначав раніше; мисливець на помилок, ймовірно, очікує, що ви зробите це за допомогою циклу for, тому їм буде легше знайти його, якщо ви будете використовувати цикл.

3
-1. Ось дві вправи для вас: а) запустіть свій код repetitions = -5. б) завантажте Commons Lang і запустіть repeatString('a', 1000)мільйон разів у циклі; зробіть те саме зі своїм кодом; порівняйте часи. Для додаткового кредиту зробіть те саме з repeatString('ab', 1000).
— ChssPly76

2
Ви стверджуєте, що ваш код тоді читає StringUtils.repeat("ab",1000)? Тому що це була моя відповідь, яку ти спростував. Він також працює краще і не має помилок.
— ChssPly76

2
Прочитайте 2 речення у питанні, яке ви цитуєте. "Я намагаюся уникати циклів за будь-яку ціну, тому що" було додано до питання як уточнення у відповідь на відповідь Ендрю Зайця після моєї відповіді - не те, що це важливо, тому що якщо позиція, яку ви займаєте, "відповідь погана, якщо цикл є використовується де завгодно "немає відповідей на питання ОП. Навіть рішення dfa - настільки винахідливі - використовують для петель всередині. "jar hell" відповіли вище; commons lang в будь-якому випадку використовується у будь-якому додатку пристойного розміру і, таким чином, не додає нової залежності.
— ChssPly76

2
@ ChssPly76 на даний момент я майже впевнений, що імагіст тролінгує. Мені справді важко бачити, як хтось міг прочитати те, що я написав, і серйозно подумати, що відповіді набрані вище.
— Етан Хайльман

1
@ ChssPly76 мої відповіді взагалі не мають циклу :-p
— fortran

1

Якщо ви знаєте лише довжину вихідного рядка (і він може бути не ділиться на довжину вхідного рядка), то використовуйте цей метод:

static String repeat(String s, int length) {
    return s.length() >= length ? s.substring(0, length) : repeat(s + s, length);
}

Демонстрація використання:

for (int i = 0; i < 50; i++)
    System.out.println(repeat("_/‾\\", i));

Не використовуйте порожні sта length> 0, оскільки в цьому випадку неможливо отримати бажаний результат.


0

ось остання Stringutils.java StringUtils.java

    public static String repeat(String str, int repeat) {
    // Performance tuned for 2.0 (JDK1.4)

    if (str == null) {
        return null;
    }
    if (repeat <= 0) {
        return EMPTY;
    }
    int inputLength = str.length();
    if (repeat == 1 || inputLength == 0) {
        return str;
    }
    if (inputLength == 1 && repeat <= PAD_LIMIT) {
        return repeat(str.charAt(0), repeat);
    }

    int outputLength = inputLength * repeat;
    switch (inputLength) {
        case 1 :
            return repeat(str.charAt(0), repeat);
        case 2 :
            char ch0 = str.charAt(0);
            char ch1 = str.charAt(1);
            char[] output2 = new char[outputLength];
            for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
                output2[i] = ch0;
                output2[i + 1] = ch1;
            }
            return new String(output2);
        default :
            StringBuilder buf = new StringBuilder(outputLength);
            for (int i = 0; i < repeat; i++) {
                buf.append(str);
            }
            return buf.toString();
    }
    }

він навіть не повинен бути таким великим, він може бути внесений до цього, і його можна скопіювати і вставити в клас корисності у вашому проекті.

    public static String repeat(String str, int num) {
    int len = num * str.length();
    StringBuilder sb = new StringBuilder(len);
    for (int i = 0; i < times; i++) {
        sb.append(str);
    }
    return sb.toString();
    }

Отже, e5, я думаю, що найкращим способом зробити це було б просто скористатися вищезгаданим кодом або будь-яким із відповідей тут. але commons lang є занадто великим, якщо це невеликий проект


Я не думаю, що є багато іншого, що ти можеш зробити ...
— alexmherrmann

0

Я створив рекурсивний метод, який виконує те саме, що ви хочете .. сміливо використовуйте це ...

public String repeat(String str, int count) {
    return count > 0 ?  repeat(str, count -1) + str: "";
}

я маю таку ж відповідь на Чи можу я помножити рядки в Java для повторення послідовностей?


Зайве перерозподіл рядків і рекурсія накладних витрат ... погано, погано, не добре.
— Олександр - Відновити Моніку

1
Це буде повільно. Не рекомендовано! Використовуйте StringBuilderзамість цього.
— Світлін Наков

0
public static String rep(int a,String k)

       {
           if(a<=0)
                return "";
           else 
           {a--;
               return k+rep(a,k);
       }

Ви можете використовувати цей рекурсивний метод для досягнення бажаної мети.

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