Простой способ повторить строку в Java


598

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

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

repeated.equals("abcabcabc");

Относится к:

Повторите строку javascript Создайте NSString, повторяя другую строку заданное количество раз

Edited

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

  1. Они добавляют к числу строк кода, даже если они спрятаны в другой функции.

  2. Кто-то, читающий мой код, должен понять, что я делаю в цикле for. Даже если он прокомментирован и имеет значимые имена переменных, они все равно должны убедиться, что он не делает ничего «умного».

  3. Программисты любят помещать умные вещи в циклы, даже если я пишу это для того, чтобы «делать только то, для чего они предназначены», что не исключает появления кого-то и добавления некоторого дополнительного умного «исправления».

  4. Их очень часто легко ошибиться. Для циклов, включающих индексы, склонны генерировать по одной ошибки.

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

  6. Для циклов увеличьте количество мест, которые должен искать охотник за насекомыми.


35
Я понимаю, что для циклов может вызвать некоторые реальные проблемы. Но вы не должны стараться избегать циклов "любой ценой", потому что, если это будет стоить вам удобочитаемости, удобства обслуживания и скорости, вы будете контрпродуктивными. Это один из таких случаев.
Imagist

9
«Они добавляют к числу строк кода, даже если они спрятаны в другой функции» ... вау, просто вау. Big-O, а не
LoC

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

3
@Pyrolistical Я не претендую на производительность или асимптотические преимущества. Скорее говоря, написав меньше кода и используя библиотечные функции, а не изобретая колесо, я уменьшаю площадь поверхности ошибки (Lines of Code), увеличивая читабельность. Обе хорошие вещи, я уверен, вы согласитесь.
Итан Хейлман

4
@ e5; прошу прощения за публикацию годами позже. Я считаю этот вопрос таким уместным. Если он вставлен в метод, аргументы должны быть проверены (времена> = 0), выданы ошибки и т. Д. Это добавляет надежности, а также строк кода для чтения. Повторение строки является чем-то однозначным. Кто читает код, точно знает, что делает string.repeat, даже без строки комментария или javadoc. Если мы используем стабильную библиотеку, разумно думать, что в такой простой функции нет ошибок, Тем не менее, вводится некоторая форма проверки «робастности», о которой нам даже нужно беспокоиться. Если бы я мог попросить 10 улучшений, эти (вроде бы) вещи были бы едины.
AgostinoX

Ответы:


230

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 9 на улице (и не буду долгое время ..) - и 11 , видимо,
готовится к отправке

1
Вероятно, очевидно, но вы также можете вызвать этот метод для строкового литерала:"abc".repeat(3)
Boann

895

Вот самая короткая версия (требуется Java 1.5+):

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

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

Нет импорта или библиотеки не требуется.


22
Я не думаю, что это вообще запутано. Примитивные типы ( char[]в данном случае) создаются с нулями, затем из Stringних создается a char[], а с нулями - replaced()тот символ, который вам нуженs
Amarok

32
Хотя это очень умно (+1), я думаю, что это в значительной степени доказывает тот факт, что циклы for часто дают более ясный код
Ричард Тингл

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 не компилируется -> Несоответствие типов: невозможно преобразовать из List <Character> в CharSequence
Arigion

6
@Arigion sдолжна быть строка, а неChar
Джанер

@ Канер Спасибо. Мой плохой, я прошу прощения. Видимо, я был слишком уставшим вчера. Извините за снижение голосов. Я уберу понижающий голос, как только смогу (он заблокирован, пока вопрос не будет отредактирован)
Arigion

@Arigion без проблем, дал понять теперь , что s является строка
Джанер

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
Хорошая особенность этого метода заключается в том, что с помощью join вы можете предоставить символ-разделитель, который очень удобен, если, например, вы создаете список CSV. Со всеми другими методами у вас есть завершающий символ присоединения, который необходимо удалить в отдельной операции.
DroidOS

102

Вот способ сделать это, используя только стандартные функции String и без явных циклов:

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

5
Удивительно :-) Хотя остерегайтесь того, что n станет нулевым…!
Ян Мейер

4
@ Виджай Дев & Фортран: Нет, он имел в виду replace(). В Java 1.5+, есть перегруженная версия , replace()которая принимает два CharSequenceс (которые включают в себя Stringы): download.oracle.com/javase/1.5.0/docs/api/java/lang/...
user102008

79
Уч. Это безобразно
Карел Билек

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

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

87

Если вы похожи на меня и хотите использовать Google Guava, а не Apache Commons. Вы можете использовать метод repeat в классе Guava Strings.

Strings.repeat("-", 60);

2
... и получите 3Mb новых зависимостей.
MonoThreaded

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 вызовов (~ 3x).
  3. Рекурсивная конкатенация линейных вызовов (~ 30x).
  4. Итеративная конкатенация линейная (~ 45х).

Я никогда не догадывался, что рекурсивная функция была быстрее forцикла: -o

Веселитесь (Ction XD).


1
+1 за рекурсию и, очевидно, быть хакером. Я не думаю, что это также неэффективно, конкатенация строк не является военным преступлением, как это было раньше, потому что + на самом деле - просто Ubuntu stringBuilder. См. Stackoverflow.com/questions/47605/java-string-concatenation и schneide.wordpress.com/2009/02/23/… . Интересно, сколько все эти стеки выталкивают и выталкивают из стоимости рекурсии, или если горячая точка заботится о них. Очень жаль, что у меня не было свободного времени, чтобы оценить его. Может кто-то еще?
Итан Хейлман

@ e5: Фортран прав; это решение может быть сделано более эффективным. Эта реализация излишне создаст новый 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
Пожалуйста, никогда не возвращайте нуль - в этом случае возвращайте пустую строку, что позволяет всегда использовать возвращаемое значение без проверки. В противном случае, что я бы рекомендовал использовать плакат.
Торбьерн Равн Андерсен

7
Ну, есть три способа обработки, если s равен нулю. 1. Передайте ошибку (верните ноль), 2. Скрыть ошибку (верните «»), 3. Бросьте NPE. Спрятать ошибку и бросить NPE не круто, поэтому я пропустил ошибку.
пиролистический

1
@EthanHeilman добавь 2MB, commons-lang3.3.1-sourcesи ты уже не так хорош;) Но если у кого-то уже есть commons-lang, я поддержу твой ответ.
TWiStErRob

9

основываясь на ответе Фортрана, это рекурсивная версия, которая использует 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

использовать Dollar просто:

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

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


3
действительно ли нужен метод assertThat ()?
ceklock 16.12.12

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

ООП решение

Почти каждый ответ предлагает статическую функцию в качестве решения, но, думая об объектно-ориентированном (для целей повторного использования и ясности), я придумал решение через делегирование через интерфейс CharSequence (который также открывает удобство использования на изменяемых классах CharSequence-Classes).

Следующий класс может использоваться с или без Separator-String / CharSequence, и каждый вызов toString () создает последнюю повторяемую строку. Input / Separator не только ограничен String-Class, но может быть любым классом, который реализует 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
редко я видел вещи, которые отвратительны: /
Ven

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

РЕДАКТИРОВАТЬ

и без петель вы можете попробовать с:

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

РЕДАКТИРОВАТЬ 2

использование коллекций еще короче:

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

Однако мне все еще нравится первая версия.


6
-1: слишком умный наполовину. Если ваша цель - сделать ваш код читабельным или эффективным, эти «решения» не очень хорошая идея. 'repeat' может быть просто переписан с использованием StringBuilder (установка начальной емкости). И 'repeat2' / 'repeat3' действительно неэффективны и зависят от неуказанного синтаксиса String, созданного String []. ToString ().
Стивен С.

@Thorb: безусловно, с этим кодом вы не можете использовать «метасимвол», [],
dfa

@Stephen: вопрос был отредактирован, чтобы явно не запрашивать циклы. Ответ на основе StringBuilder уже был предоставлен, поэтому я избежал публикации дубликата
dfa

@ Стефан: я не могу понять, понижающий голос. Мой отредактированный ответ без петель, как требуется. Нет никаких запросов об эффективности. Я думаю, что этот вопрос - всего лишь интеллектуальная попытка произвести объединение без цикла.
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
В моем более экстремальном тесте я получаю 1700000000 (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.
Behrouz.M

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): "";
Феринг

О, кажется 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();
}

Ваши причины не использовать цикл for не очень хорошие. В ответ на вашу критику:

  1. Какое бы решение вы ни использовали, оно почти наверняка будет длиннее. Использование предварительно встроенной функции только подправляет ее под большее покрытие.
  2. Кто-то, читающий ваш код, должен будет понять, что вы делаете в этом цикле. Учитывая, что цикл for является идиоматическим способом сделать это, было бы гораздо проще понять, если бы вы сделали это с помощью цикла for.
  3. Да кто - то может добавить что - то умное, но, избегая для цикла вы будете делать что - то умное . Это все равно, что умышленно выстрелить себе в ногу, чтобы случайно не выстрелить себе в ногу.
  4. Одиночные ошибки также легко понять с помощью одного теста. Учитывая, что вы должны тестировать свой код, легко исправить и отловить ошибку «один за другим». И это стоит отметить: приведенный выше код не содержит ошибки «один за другим». Для петель одинаково легко получить права.
  5. Так что не используйте переменные повторно. Это не ошибка цикла.
  6. Опять же, как и любое решение, которое вы используете. И, как я уже отмечал ранее; охотник за ошибками, вероятно, будет ожидать, что вы сделаете это с циклом for, поэтому им будет легче найти его, если вы используете цикл for.

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

2
Вы утверждаете, что ваш код более читабелен StringUtils.repeat("ab",1000)? Потому что это был мой ответ, что ты проголосовал. Он также работает лучше и не имеет ошибок.
ChssPly76

2
Прочитайте 2-е предложение в вопросе, который вы цитируете. «Я стараюсь избегать циклов любой ценой, потому что» был добавлен к вопросу в качестве разъяснения в ответ на ответ Эндрю Хэра после моего ответа - не то, чтобы это имело значение, потому что, если позиция, которую вы занимаете, «ответ плохой, если цикл используется везде "нет ответов на вопрос ОП. Даже решения dfa, как бы они ни были изобретательны, используют внутри петли. на «банку ада» ответили выше; 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, я думаю, что лучший способ сделать это - просто использовать вышеупомянутый код или любой из ответов здесь. но общий язык слишком велик, если это маленький проект


Я не думаю, что есть еще что-то, что вы можете сделать ... кроме AOT!
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.