Простий спосіб повторити струну в 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.