Prosty sposób na powtórzenie ciągu w Javie

597

Szukam prostej wspólnej metody lub operatora, który pozwala mi powtarzać niektóre ciągi n razy. Wiem, że mógłbym to napisać za pomocą pętli for, ale chciałbym unikać pętli w razie potrzeby i gdzieś powinna istnieć prosta metoda bezpośrednia.

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

repeated.equals("abcabcabc");

Związany z:

powtórz ciąg javascript Utwórz NSString, powtarzając kolejny ciąg znaków określoną liczbę razy

Edytowane

Staram się unikać pętli, gdy nie są one całkowicie konieczne, ponieważ:

  1. Zwiększają liczbę wierszy kodu, nawet jeśli są ukryte w innej funkcji.

  2. Ktoś czytający mój kod musi dowiedzieć się, co robię w pętli for. Nawet jeśli jest komentowany i ma sensowne nazwy zmiennych, wciąż muszą się upewnić, że nie robi nic „mądrego”.

  3. Programiści uwielbiają umieszczać sprytne rzeczy w pętlach, nawet jeśli piszę to, aby „robić tylko to, co jest przeznaczone”, co nie wyklucza, że ​​ktoś przyjdzie i doda kilka dodatkowych sprytnych „poprawek”.

  4. Bardzo często łatwo się pomylić. Pętle zawierające indeksy zwykle generują jeden błąd.

  5. W przypadku pętli często ponownie używaj tych samych zmiennych, zwiększając szansę naprawdę trudnego znalezienia błędów dotyczących zakresu.

  6. W przypadku pętli zwiększ liczbę miejsc, w których łowca błędów musi szukać.

Ethan Heilman
źródło
35
Rozumiem, że pętle mogą powodować pewne prawdziwe problemy. Ale nie powinieneś próbować unikać pętli „za wszelką cenę”, ponieważ jeśli kosztuje to czytelność, łatwość konserwacji i szybkość, osiągasz efekt przeciwny do zamierzonego. To jest jeden z tych przypadków.
Imagist
9
„Dodają do liczby wierszy kodu, nawet jeśli są ukryte w innej funkcji”… wow, po prostu wow. Big-O, nie LoC
pirolistyczny
7
@imagist Unikam pętli w sytuacjach, w których kosztuje mnie to czytelność i łatwość konserwacji. Uważam szybkość za najmniej istotną kwestię w tym przypadku (w rzeczywistości nie jest problemem). Myślę, że pętle są nadużywane i próbuję nauczyć się używać pętli tylko wtedy, gdy są one konieczne, a nie jako rozwiązanie domyślne.
Ethan Heilman,
3
@Pirolistyczny Nie twierdzę, że mam wydajność lub asymptotyczne korzyści. Raczej mówiąc, pisząc mniej kodu i używając funkcji bibliotecznych zamiast wymyślać koło, zmniejszam powierzchnię błędu (linie kodu), zwiększając jednocześnie czytelność. Obie dobre rzeczy, jestem pewien, że się zgodzisz.
Ethan Heilman,
4
@ e5; przepraszam za publikowanie lat później. Uważam to pytanie za odpowiednie. Jeśli wstawione do metody, argumenty powinny zostać przetestowane (razy> = 0), zgłoszone błędy itp. Dodaje to solidności, ale także wierszy kodu do odczytania. Powtarzanie łańcucha jest czymś jednoznacznym. Kto czyta kod, wie dokładnie, co robi łańcuch. Powtórzenie, nawet bez wiersza komentarza lub javadoc. Jeśli używamy stabilnej biblioteki, rozsądnie jest myśleć, że tak prosta funkcja nie zawiera błędów, JESZCZE wprowadza pewną formę sprawdzania „odporności”, o którą nawet musimy się martwić. Gdybym mógł poprosić o 10 ulepszeń, te (rodzaj) rzeczy byłyby jednym.
AgostinoX,

Odpowiedzi:

229

String::repeat

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

Nowością w Javie 11 jest metoda, String::repeatktóra wykonuje dokładnie to, o co prosiłeś:

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

Jego Javadoc mówi:

/**
 * 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
 */ 
Nicolai
źródło
2
@ Kod źródłowy Nicolai, na wypadek, gdyby ktoś się przejmował hg.openjdk.java.net/jdk/jdk/file/fc16b5f193c7/src/java.base/…
Eugene
7
jeszcze nie widziałem java 9 jeszcze na ulicy (i nie będę długo czekać ..) - i 11 jest prawdopodobnie ustawiony na wysyłkę ..
javadba
1
Prawdopodobnie oczywiste, ale tę metodę można także nazwać dosłownym ciągiem znaków:"abc".repeat(3)
Boann
895

Oto najkrótsza wersja (wymagana Java 1.5+):

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

Gdzie njest ile razy chcesz powtórzyć ciąg i sjest ciąg do powtórzenia.

Nie wymaga importu ani bibliotek.

użytkownik102008
źródło
22
Nie wydaje mi się, żeby to było zaciemnione. Typy pierwotne ( char[]w tym przypadku) są tworzone z wartościami null, następnie a Stringjest tworzone z char[], a wartości null są replaced()z postacią, w której chceszs
Amarok
32
Chociaż jest to bardzo mądry (+1) Myślę, że to dość dużo dowodzi tego stopnia, że na pętli często powodują wyraźniejszego kodu
Richard Tingle
75
Dla tych, którzy narzekają na zaciemnianie, czytelność zależy od umiejętności czytania i pisania. Jest to całkowicie jasne w tym, co robi i edukacyjnych dla tych, którzy mogą tego nie zobaczyć od razu. Tak przy okazji, otrzymujesz to z Javą.
dansalmo
11
Aby uzyskać lepszą wydajność, ...replace('\0', str)należy użyć wersji String.
user686249,
11
@ user686249: Istnieje tylko replace(char oldChar, char newChar)i replace(CharSequence target, CharSequence replacement)tak nie widzę, w jaki sposób, który mógłby pracować
user102008
334

Jeśli używasz Java <= 7 , jest to tak zwięzłe, jak to możliwe:

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

W Javie 8 i nowszych istnieje bardziej czytelny sposób:

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

Wreszcie, w Javie 11 i nowszych, istnieje nowa repeat​(int count)metoda specjalnie dla tego ( link )

"abc".repeat(12);

Alternatywnie, jeśli twój projekt korzysta z bibliotek Java, istnieje więcej opcji.

Dla Apache Commons :

StringUtils.repeat("abc", 12);

W przypadku Google Guava :

Strings.repeat("abc", 12);
Caner
źródło
4
Ten pierwszy powoduje wyjątek, gdy nwynosi zero.
saka1029
Przykład java 8 nie kompiluje się -> Niezgodność typu: nie można przekonwertować z List <Character> na CharSequence
Arigion
6
@Arigion smusi być String, a nieChar
Caner
@Caner Thanks. Przepraszam, przepraszam. Najwyraźniej wczoraj byłem zbyt zmęczony. Przepraszam za przegłosowanie. Usunę głosowanie tak szybko, jak to możliwe (jest zablokowane, dopóki pytanie nie zostanie zredagowane)
Arigion
@Arigion nie ma problemu, wyjaśniłem teraz, że s jest ciągiem
Caner
312

Commons Lang StringUtils.repeat ()

Stosowanie:

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

repeated.equals("abcabcabc");
ChssPly76
źródło
99
użycie zależności od jednej metody dla uproszczenia w dłuższej perspektywie może doprowadzić do słoika
dfa
81
Jasne, tyle że to wspólny język. Chyba nigdy nie widziałem projektu o ponad 5000 LOCS, który nie miałby wspólnego języka.
Ethan Heilman,
11
Commons Lang jest oprogramowaniem typu open source - pobierz go i zobacz. Oczywiście ma wewnątrz pętlę, ale nie jest tak prosta. Dużo wysiłku włożono w profilowanie i optymalizację tej implementacji.
ChssPly76,
28
Nie unikam pętli ze względu na wydajność (przeczytaj moje powody w pytaniu). Kiedy ktoś widzi StringUtils.repeat, wie, co robię. Nie muszą się martwić, że próbowałem napisać własną wersję powtórzenia i popełniłem błąd. To atomowa jednostka poznawcza!
Ethan Heilman,
7
@ Thorbjørn Ravn Andersen - może stać się o wiele bardziej interesujący, jeśli sprawy zostaną wyjęte z kontekstu
ChssPly76,
140

Java 8 String.joinzapewnia prosty sposób na zrobienie tego w połączeniu z Collections.nCopies:

// say hello 100 times
System.out.println(String.join("", Collections.nCopies(100, "hello")));
Boann
źródło
7
Dziękuję Ci! Dla Androida TextUtils.join () może być użyty zamiast String.join ()
MinaHany
2
Dziękuję za tę odpowiedź. Wydaje się, że jest to najczystszy sposób bez użycia zewnętrznej metody API lub narzędzia! bardzo dobre!!
Andreas M. Oberheim
2
Zaletą tej metody jest to, że z łączeniem możesz podać znak separatora, który okazuje się bardzo przydatny, jeśli, powiedzmy, budujesz listę CSV. We wszystkich pozostałych metodach masz łączący znak kończący, który należy usunąć w oddzielnej operacji.
DroidOS,
102

Oto sposób na zrobienie tego przy użyciu tylko standardowych funkcji String i bez wyraźnych pętli:

// create a string made up of  n  copies of  s
repeated = String.format(String.format("%%%ds", n), " ").replace(" ",s);
IJ Kennedy
źródło
5
Niesamowite :-) Chociaż uważaj, aby n nie było zerowe…!
Yang Meyer,
4
@Vijay Dev & fortran: Nie, miał na myśli replace(). W Javie 1.5+ istnieje przeciążona wersja, replace()która zajmuje dwa CharSequences (w tym Strings): download.oracle.com/javase/1.5.0/docs/api/java/lang/…
user102008
79
Auć. To jest brzydkie.
Karel Bílek
8
@ mzuba powiedzmy n=3: najpierw formatuje ciąg, aby wyglądał mniej więcej %03d( %%to jest znak ucieczki od znaku procentu), który jest kodem formatującym, aby dodać 3 zera dopełniające, a następnie formatuje 0z tym, prowadząc do 000, a na końcu zamienia 0je na ciągi
fortran
15
Możesz sprawić, by rozwiązanie było mniej brzydkie i łatwiejsze do zrozumienia: String.format („% 0” + n + „d”, 0) .replace („0”, s)
Artur,
87

Jeśli jesteś podobny do mnie i chcesz korzystać z Google Guava, a nie Apache Commons. Możesz użyć metody repeat w klasie Struny Guava.

Strings.repeat("-", 60);
Jacek
źródło
2
... i uzyskaj 3 MB nowych zależności.
MonoThreaded
6
@MonoThreaded Myślałem, że to pójdzie bez słowa, ale nie dołączaj guawy tylko po to, aby powtórzyć ciąg znaków. Moja odpowiedź brzmiała: jeśli i tak już używasz guawy, to właśnie tak byś to zrobił.
Jack
53

Z , możesz także użyć Stream.generate.

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

w razie potrzeby możesz owinąć go prostą metodą narzędziową:

public static String repeat(String str, int times) {
   return Stream.generate(() -> str).limit(times).collect(joining());
}
Alexis C.
źródło
6
... lub return IntStream.range(0, times).mapToObj(i -> str).collect(joining());który lepiej zrównuje się z sobą
Alexis C.,
32

Więc chcesz uniknąć pętli?

Oto masz:

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

(oczywiście wiem, że to brzydkie i nieefektywne, ale nie ma pętli :-p)

Chcesz, żeby było to prostsze i ładniejsze? użyj jython:

s * 3

Edycja : zoptymalizujmy to trochę :-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 : Zrobiłem szybki i brudny test porównawczy dla 4 głównych alternatyw, ale nie mam czasu, aby uruchomić go kilka razy, aby uzyskać środki i zaplanować czasy dla kilku danych wejściowych ... Więc oto kod, jeśli ktoś chce spróbować tego:

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

Wymaga 2 argumentów, pierwszy to liczba iteracji (każda funkcja działa z czasami powtarzania arg od 1..n), a drugi to ciąg do powtórzenia.

Jak na razie szybka kontrola czasów z różnymi danymi wejściowymi pozostawia w rankingu coś takiego (lepiej na gorsze):

  1. Iteracyjna aplikacja StringBuilder (1x).
  2. Rekurencyjne wywołania log2 konkatenacji (~ 3x).
  3. Rekurencyjne wywołania liniowe konkatenacji (~ 30x).
  4. Iatacyjna konkatenacja liniowa (~ 45x).

Nigdy bym nie zgadł, że funkcja rekurencyjna była szybsza niż forpętla: -o

Baw się dobrze (ctional xD).

fortran
źródło
1
+1 za rekurencję i oczywiście bycie hakerem seplenienia. Nie wydaje mi się, żeby było to tak nieefektywne, konkatenacja łańcuchów nie jest zbrodnią wojenną, jaką kiedyś była, ponieważ + tak naprawdę to po prostu stringBuilder UTH. Zobacz stackoverflow.com/questions/47605/java-string-concatenation i schneide.wordpress.com/2009/02/23/… . Zastanawiam się, ile kosztują wszystkie te stosy i wyskakuje z kosztu rekurencji, czy też zajmuje się nimi hotspot. Naprawdę żałuję, że nie miałem czasu, aby to sprawdzić. Może ktoś jeszcze?
Ethan Heilman,
@ e5: fortran ma rację; to rozwiązanie może być bardziej wydajne. Ta implementacja niepotrzebnie utworzy nowy StringBuilder (i nowy ciąg) dla każdej rekurencji. Jednak wciąż dobre rozwiązanie.
Rob
3
@ e5 Chciałbym być hakerem Lisp xD ... Gdybym tak był, użyłbym funkcji rekurencyjnej ogona :-p
fortran
1
Mikrodrobne znaki nie działają dobrze w Javie. Próba zmierzenia takiej prędkości implementacji nie jest dobra.
Ceklock
@tecnotron Wiem, ale wciąż są lepsze niż nic ... I jedyną „niespodzianką” była niewielka różnica między naiwnym połączeniem pętli a rekurencją liniową.
fortran
20

Zawiera mniej znaków niż twoje pytanie

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();
}
Pirolistyczny
źródło
4
Zawiera więcej znaków niż moja odpowiedź StringUtils.repeat (str, n).
Ethan Heilman,
8
O ile nie korzystasz już z Apache Commons, ta odpowiedź jest o wiele mniej kłopotliwa - nie trzeba pobierać innej biblioteki, włączając ją w ścieżkę klasy, upewniając się, że jej licencja jest kompatybilna z twoją, itp.
Paul Tomblin
7
Proszę, nigdy nie zwracaj null - w takim przypadku zwróć pusty ciąg znaków, co pozwoli ci zawsze używać niezaznaczonej wartości. W przeciwnym razie poleciłbym plakat.
Thorbjørn Ravn Andersen
7
Istnieją trzy sposoby radzenia sobie, jeśli s ma wartość NULL. 1. Przekaż błąd (zwróć null), 2. Ukryj błąd (zwróć „”), 3. Rzuć NPE. Ukrywanie błędu i rzucanie NPE nie jest fajne, więc przeszedłem błąd.
Pyrolistic
1
@EthanHeilman dodaje wartość 2 MB commons-lang3.3.1-sourcesi nie jesteś już taki dobry;) Ale jeśli ktoś już to ma commons-lang, popieram twoją odpowiedź.
TWiStErRob
9

w oparciu o odpowiedź fortrana jest to wersja wielokrotnego użytku, która korzysta z 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();
}
dfa
źródło
1
zapętlanie zamiast rekurencji zmniejszyłoby liczbę ramek stosu dla dużej liczby powtórzeń.
La-comadreja
7

używanie Dollara jest proste jak pisanie:

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

PS: powtarzanie działa również dla tablicy, listy, zestawu itp

dfa
źródło
3
czy metoda assertThat () jest naprawdę potrzebna?
Ceklock
7

Chciałem, aby funkcja utworzyła rozdzieloną przecinkami listę znaków zapytania do celów JDBC, i znalazłem ten post. Postanowiłem więc wziąć dwa warianty i sprawdzić, który z nich działa lepiej. Po milionie iteracji odmiana ogrodowa StringBuilder zajęła 2 sekundy (fun1), a tajemnicza, bardziej optymalna wersja (fun2) zajęła 30 sekund. Po co znów być tajemniczym?

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);
}
Kok
źródło
3
Mam sens, że druga zajęłaby znacznie dłużej. Wykonuje wyszukiwanie łańcucha, a następnie modyfikuje ciąg znaków po znaku.
Ethan Heilman,
7

Rozwiązanie OOP

Niemal każda odpowiedź proponuje funkcję statyczną jako rozwiązanie, ale myśląc o obiektach (dla celów ponownego użycia i przejrzystości) wymyśliłem rozwiązanie poprzez delegację poprzez interfejs CharSequence (który otwiera także użyteczność na zmiennych klasach CharSequence).

Poniższej klasy można używać zarówno z Separator-String / CharSequence, jak i bez niej, a każde wywołanie funkcji „toString ()” tworzy końcowy powtarzany ciąg. Input / Separator nie są ograniczone tylko do String-Class, ale mogą być każdą klasą, która implementuje CharSequence (np. StringBuilder, StringBuffer itp.)!

Kod źródłowy:

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

Przykład użycia:

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

Przykładowy wynik:

Repeat=2    Separator=<=    Input=123   Length=3
CharSeq:    Length=8    Val=123<=123
String :    Length=8    Val=123<=123
123ff<=123ff
123ff<====>123ff
Maciej Schuttkowski
źródło
4
rzadko widziałem rzeczy obrzydliwe: /
Ven
6

używając tylko klas JRE ( System.arraycopy ) i próbując zminimalizować liczbę obiektów tymczasowych, możesz napisać coś takiego:

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

EDYTOWAĆ

i bez pętli możesz wypróbować:

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

EDYCJA 2

korzystanie z kolekcji jest jeszcze krótsze:

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

jednak nadal lubię pierwszą wersję.

dfa
źródło
6
-1: zbyt sprytny o połowę. Jeśli Twoim celem jest uczynienie kodu czytelnym lub wydajnym, te „rozwiązania” nie są dobrym pomysłem. „repeat” można po prostu przepisać przy użyciu StringBuilder (ustawiając początkową pojemność). A „repeat2” / „repeat3” są naprawdę nieefektywne i zależą od nieokreślonej składni String wytwarzanej przez String []. ToString ().
Stephen C
@Thorb: absolutnie, z tym kodem nie można używać „metacharacter”, [],
dfa
@ Stephen: pytanie zostało edytowane, aby jawnie żądać braku pętli. Odpowiedź oparta na StringBuilder została już dostarczona, więc uniknąłem opublikowania duplikatu
dfa
@Stephan: Nie mogę rozgryźć opinii. Moja zredagowana odpowiedź jest wolna od pętli, zgodnie z wymaganiami. Nie ma żadnych żądań dotyczących wydajności. Myślę, że to pytanie jest tylko intelektualnym wysiłkiem, aby uzyskać konkatenację bez pętli.
dfa
@Stephan: Łańcuch wytworzony przez Collection.toString (i Arrays.toString) jest jasno określony w AbstractCollection.toString: „Reprezentacja łańcucha składa się z listy elementów kolekcji w kolejności, w jakiej są zwracane przez iterator, ujęta w nawiasy kwadratowe ( „[]”). Sąsiadujące elementy są oddzielone znakami „,” (przecinek i spacja). ”
dfa
6

Nie najkrótszy, ale (myślę) najszybszym sposobem jest użycie 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();
  }
Szymon
źródło
5

Jeśli zależy Ci na szybkości, powinieneś zużywać jak najmniej kopii do pamięci. Dlatego wymagana jest praca z tablicami znaków.

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

Aby przetestować prędkość, podobna optymalna metoda przy użyciu StirngBuilder wygląda następująco:

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

i kod do przetestowania:

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

}

A oto wyniki z mojego systemu:

elapsed repeatString: 6006571
elapsed repeatStringSB: 9064937

Zauważ, że testem pętli jest uruchomienie JIT i uzyskanie optymalnych wyników.

Panayotis
źródło
4

ze względu na czytelność i przenośność:

public String repeat(String str, int count){
    if(count <= 0) {return "";}
    return new String(new char[count]).replace("\0", str);
}
Martin Zeitler
źródło
3

Jeśli martwisz się wydajnością, po prostu użyj StringBuilder wewnątrz pętli i wykonaj .toString () na wyjściu z pętli. Heck, napisz własną klasę util i użyj jej ponownie. 5 linii kodu maks.

WolfmanDragon
źródło
2

Naprawdę podoba mi się to pytanie. Jest dużo wiedzy i stylów. Więc nie mogę tego zostawić bez pokazania mojego rock and rolla;)

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

Czy lubisz to?

Daniel De León
źródło
1
W moim bardziej ekstremalnym teście produkuję ciąg 1700000000 (1,7 gigasa) długości powtórzeń, używając -Xms4937m
Daniel De León
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);
}
sokół
źródło
2

Prosta pętla

public static String repeat(String string, int times) {
    StringBuilder out = new StringBuilder();
    while (times-- > 0) {
        out.append(string);
    }
    return out.toString();
}
Codler
źródło
2
przekazać timesdo konstruktora StringBuilder.
Behrouz.M
2

Wypróbuj to:

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

źródło
2

Używając rekurencji, możesz wykonać następujące czynności (używając operatorów trójskładnikowych, maksymalnie jedna linia):

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

Wiem, jest brzydka i prawdopodobnie nieefektywna, ale to jedna linia!

HyperNeutrino
źródło
Takie podejście wybrałbym, ale po co przeprowadzać więcej kontroli niż potrzeba? zwracana liczba> 0? string + repeat (string, number-1): "";
Fering
Och, wygląda na to, że odpowiedziałem na to poniżej
Fering
@ Podaj główny powód, aby w ten sposób zawsze była średnia O (log N), a nie O (N). Nieco lepsza optymalizacja niż druga, ale mimo to wciąż kiepska.
HyperNeutrino,
2

proste jednoliniowe rozwiązanie:
wymaga Java 8

Collections.nCopies( 3, "abc" ).stream().collect( Collectors.joining() );
Kaplan
źródło
1

Mimo że nie chcesz używać pętli, myślę, że powinieneś użyć pętli.

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

Powody, dla których nie używasz pętli for, nie są dobre. W odpowiedzi na twoją krytykę:

  1. Każde zastosowane rozwiązanie prawie na pewno będzie dłuższe. Korzystanie z wbudowanej funkcji powoduje, że jest ona umieszczana tylko pod większą ilością osłon.
  2. Ktoś czytający twój kod będzie musiał dowiedzieć się, co robisz w tym non-for-loop. Biorąc pod uwagę, że pętla for jest idiomatycznym sposobem na zrobienie tego, o wiele łatwiej byłoby dowiedzieć się, czy zrobiłbyś to z pętlą for.
  3. Tak ktoś może dodać coś mądrego, ale poprzez unikanie pętli for ty się robi coś mądrego . To tak, jakby celowo strzelać sobie w stopę, aby uniknąć przypadkowego wystrzelenia w stopę.
  4. Błędy jednorazowe są również zaskakująco łatwe do wykrycia za pomocą jednego testu. Biorąc pod uwagę, że powinieneś testować swój kod, błąd off-by-one powinien być łatwy do naprawienia i wykrycia. I warto zauważyć: powyższy kod nie zawiera błędu „jeden po drugim”. Pętle są równie łatwe do zrobienia.
  5. Więc nie używaj ponownie zmiennych. To nie jest wina pętli for.
  6. Ponownie, podobnie jak każde zastosowane rozwiązanie. I jak zauważyłem wcześniej; Łowca błędów prawdopodobnie spodziewa się, że zrobisz to z pętlą for, więc łatwiej będzie ci go znaleźć, jeśli użyjesz pętli for.
Imagist
źródło
3
-1. Oto dwa ćwiczenia dla Ciebie: a) uruchom swój kod repetitions = -5. b) pobierz Commons Lang i uruchom repeatString('a', 1000)milion razy w pętli; zrób to samo ze swoim kodem; porównaj czasy. Aby uzyskać dodatkowy kredyt, zrób to samo z repeatString('ab', 1000).
ChssPly76,
2
Czy argumentujesz, że Twój kod jest bardziej czytelny StringUtils.repeat("ab",1000)? Ponieważ to była moja odpowiedź, którą głosowałeś. Działa również lepiej i nie ma błędów.
ChssPly76
2
Przeczytaj drugie zdanie cytowanego pytania. „Staram się unikać pętli za wszelką cenę, ponieważ” zostało dodane do pytania jako wyjaśnienie w odpowiedzi na odpowiedź Andrew Hare'a po mojej odpowiedzi - nie ma to znaczenia, ponieważ jeśli zajmowane stanowisko to „odpowiedź jest zła, jeśli pętla jest używane wszędzie ”nie ma odpowiedzi na pytanie OP. Nawet rozwiązania dfa - choć pomysłowe - nadają się do pętli wewnątrz. „piekło ze słoika” zostało udzielone powyżej; commons lang i tak jest używany w każdej aplikacji o przyzwoitej wielkości, a zatem nie dodaje nowej zależności.
ChssPly76,
2
@ ChssPly76 w tym momencie jestem pewien, że wyobraźnia trolluje. Naprawdę trudno mi zrozumieć, jak ktokolwiek mógł przeczytać to, co napisałem i poważnie pomyśleć o odpowiedziach wpisanych powyżej.
Ethan Heilman,
1
@ ChssPly76 moje odpowiedzi nie mają żadnych pętli :-p
fortran
1

Jeśli znasz tylko długość ciągu wyjściowego (i może nie być podzielna przez długość ciągu wejściowego), użyj tej metody:

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

Demo użycia:

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

Nie używaj z pustymi si length> 0, ponieważ w tym przypadku nie można uzyskać pożądanego wyniku.

John McClane
źródło
0

oto najnowszy 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();
    }
    }

nie musi być nawet tak duży, można go w to przekształcić i można go skopiować i wkleić do klasy użytkowej w projekcie.

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

Więc e5, myślę, że najlepszym sposobem na to byłoby po prostu użycie wyżej wspomnianego kodu lub dowolnej z odpowiedzi tutaj. ale wspólny język jest po prostu zbyt duży, jeśli jest to mały projekt

alexmherrmann
źródło
Nie sądzę, że jest wiele innych rzeczy, które możesz zrobić ... excet może AOT !!
alexmherrmann
0

Stworzyłem metodę rekurencyjną, która robi to samo, co chcesz .. możesz użyć tego ...

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

mam tę samą odpowiedź na Czy mogę pomnożyć ciągi w Javie, aby powtarzać sekwencje?

niczm25
źródło
Niepotrzebne przesunięcie łańcucha i nadwyżka rekurencji ... źle, źle, źle.
Alexander - Przywróć Monikę
1
To będzie wolne. Niepolecane! Użyj StringBuilderzamiast tego.
Svetlin Nakov
0
public static String rep(int a,String k)

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

Możesz użyć tej metody rekurencyjnej do osiągnięcia pożądanego celu.

Syed Salman Hassan
źródło