Utwórz ciąg zawierający n znaków

144

Czy w Javie istnieje sposób na utworzenie łańcucha o określonej liczbie określonego znaku? W moim przypadku musiałbym utworzyć ciąg z 10 spacjami. Mój obecny kod to:

StringBuffer outputBuffer = new StringBuffer(length);
for (int i = 0; i < length; i++){
   outputBuffer.append(" ");
}
return outputBuffer.toString();

Czy istnieje lepszy sposób na osiągnięcie tego samego. W szczególności chciałbym coś, co jest szybkie (pod względem wykonania).

Krzyż
źródło
1
Jeśli często to robisz, po prostu napisz funkcję: String characterRepeat (Char c, int Length) {...}, która robi to, co robisz dla dowolnego znaku i dowolnej długości. Następnie po prostu zadzwoń, kiedy będziesz tego potrzebować.
Austin Fitzpatrick
12
chcesz użyć StringBuilder zamiast StringBuffer
Dodaj na początku rozmiar bufora, jest łatwy do obliczenia i ułatwia zarządzanie pamięcią! StringBuilder outputBuffer = new StringBuilder (repeat * base.length ());
Victor
1
Zobacz też: stackoverflow.com/questions/1235179/…
Dave Jarvis
Jeśli chcesz dodać pojedynczą spację, użyj append(' ')zamiast tego ... wymaga to trochę mniej przetwarzania ...
Erk,

Odpowiedzi:

36

Pętla for zostanie zoptymalizowana przez kompilator. W takich przypadkach jak Twój nie musisz samodzielnie zajmować się optymalizacją. Zaufaj kompilatorowi.

Swoją drogą, jeśli istnieje sposób na utworzenie łańcucha zawierającego n znaków spacji, jest on zakodowany w taki sam sposób, jak przed chwilą.

kalkin
źródło
1
nawet jeśli nie zostało to zoptymalizowane - jak często jest to tworzone w twoim programie - jeśli często zapisujesz w zmiennej statycznej lub innej pamięci podręcznej
mmmmmm
btw Array.fill () po prostu przechodzi przez tablicę. / ja potrzebuję zdecydowanie więcej punktów, żeby komentować inne posty :)
kalkin
@Mark Length jest zmienne, więc zapisywanie go wydaje się głupie. Co bym zrobił, miałbym static Dictionary<int, String>?
C. Ross
1
Przechowuj najdłuższy ciąg, jaki kiedykolwiek chcesz, a następnie użyj czegoś takiego: " ".substring(0, 10);
mjaggard
169

Prawdopodobnie najkrótszy kod korzystający Stringwyłącznie z interfejsu API:

String space10 = new String(new char[10]).replace('\0', ' ');

System.out.println("[" + space10 + "]");
// prints "[          ]"

Jako metoda, bez bezpośredniego tworzenia instancji char:

import java.nio.CharBuffer;

/**
 * Creates a string of spaces that is 'spaces' spaces long.
 *
 * @param spaces The number of spaces to add to the string.
 */
public String spaces( int spaces ) {
  return CharBuffer.allocate( spaces ).toString().replace( '\0', ' ' );
}

Wywołaj za pomocą:

System.out.printf( "[%s]%n", spaces( 10 ) );
smary wielogenowe
źródło
4
Mimo, że podoba mi się jednolinijkowy, myślę, że rozwiązanie FrustratedWithFormsDes jest lepsze, jeśli chodzi o wykonanie, ponieważ unika sprawdzania każdego z \ 0 i po prostu przypisuje spację.
Bouncner
1
Testowane, jest to zawsze szybsze niż pętla, dość konsekwentnie 50000-100000 nano sekund szybciej, co może być dość znaczące (0,1 milisekundy), pętla robi się szybciej, gdy liczba iteracji wzrasta, chociaż całkowity czas jest nadal wysoki. Dotyczy to tworzenia różnych rozmiarów ciągów odstępów.
kmecpp
wbij się w całkiem niezłe rozwiązanie, użyłem go w mojej odpowiedzi tutaj stackoverflow.com/questions/852665/…
maytham-ɯɐɥʇʎɐɯ
66

Hmm teraz, kiedy o tym myślę, może Arrays.fill:

char[] charArray = new char[length];
Arrays.fill(charArray, ' ');
String str = new String(charArray);

Oczywiście zakładam, że fillmetoda robi to samo, co twój kod, więc prawdopodobnie będzie działać mniej więcej tak samo, ale przynajmniej jest to mniej wierszy.

FrustratedWithFormsDesigner
źródło
2
Po sprawdzeniu źródła wydaje się, że faktycznie robi to dokładnie to, co opublikował OP: wiersz 806 z docjar.com/html/api/java/util/Arrays.java.html
Pops
2
@Lord Torgamus Czy pytanie OP było edytowane? Ponieważ w wersji, którą widzę, zapętla StringBuffer.append (), a wersja Frustrated wykonuje wypełnienie (co oczywiście zapętla przypisywanie znaków do tablicy). Wcale nie to samo.
CPerkins
@CPerkins, w porządku, nie było jasne. Chodzi mi o to, że oba z nich wprowadzają znak po znaku wewnątrz pętli for.
Wyskakuje
1
@Lord Torgamus - Zgadzam się z tym. Szkoda, że ​​jvm nie może po prostu zrobić memsetu. Cena jaką płacimy za szerokie znaki.
CPerkins
2
@Pops warto zauważyć, że jeśli Arrays.fill()funkcja jest konsekwentnie używana do wykonywania tej pracy w kodzie i (standardowym i zewnętrznym) kodzie biblioteki, to jest dobrym kandydatem do kompilacji Hotspot i ma większe szanse na znalezienie pamięci podręcznej procesora. Przyszłe maszyny JVM mogą przypisać to jako wewnętrzną funkcję.
Wykręcenie
65

Gorąco radzę nie pisać pętli ręcznie. Będziesz to robić w kółko w trakcie swojej kariery programistycznej. Osoby czytające Twój kod - w tym również Ciebie - zawsze muszą poświęcić czas, nawet jeśli jest to tylko kilka sekund, aby przetrawić znaczenie pętli.

Zamiast tego użyj jednej z dostępnych bibliotek dostarczających kod, który robi to samo, jak StringUtils.repeatz Apache Commons Lang :

StringUtils.repeat(' ', length);

W ten sposób nie musisz martwić się o wydajność, dlatego wszystkie krwawe szczegóły StringBuilder, optymalizacje kompilatora itp. Są ukryte. Gdyby funkcja działała tak wolno, byłby to błąd w bibliotece.

Dzięki Java 11 staje się to jeszcze łatwiejsze:

" ".repeat(length);
mfuchs
źródło
5
Z drugiej strony, jeśli StringUtils nie jest już używany w projekcie, dodanie kolejnej zależności dla tak prostego zadania może być przesadą.
Erich Kitzmueller
1
jeśli okazało się wolne, możesz również przesłać własną poprawkę dla funkcji.
będzie
26

W Javie 8 możesz użyć String.join:

String.join("", Collections.nCopies(n, s));
Vitalii Fedorenko
źródło
13

Jeśli chcesz tylko spacje, co powiesz na:

String spaces = (n==0)?"":String.format("%"+n+"s", "");

co da w wyniku abs (n) spacje;

BryceCicada
źródło
6
Jak to się ma do szybkości? Mam wrażenie, że format jest stosunkowo wolny. Musi jednak przeanalizować ciąg, zanim będzie mógł go utworzyć.
C. Ross
11

od wersji Java 11 :

" ".repeat(10);

od wersji Java 8 :

generate(() -> " ").limit(10).collect(joining());

gdzie:

import static java.util.stream.Collectors.joining;
import static java.util.stream.Stream.generate;
epoks
źródło
9

Od wersji Java 11 możesz po prostu użyć go String.repeat(count)do rozwiązania problemu.

Zwraca ciąg, którego wartość jest wielokrotnym konkatenacją tego ciągu count.

Jeśli ten ciąg jest pusty lub countma wartość zero, zwracany jest pusty ciąg.

Więc zamiast pętli, twój kod wyglądałby tak:

" ".repeat(length);
Samuel Philipp
źródło
8

Myślę, że jest to mniej kodu, jaki jest możliwy, używa klasy Guava Joiner:

Joiner .on (""). Join ( Collections.nCopies (10, ""));

meilechh
źródło
Najkrótsza linia kodu, ale dodanie dużej biblioteki tylko do tego prostego zadania nie ma sensu. Mogę samodzielnie stworzyć mniejszy plik JAR za pomocą jednej metody, pubic String n(int n) { ... }która pozwoli na użycie nawet „mniejszej” ilości kodu: n(10)ale znowu nie ma to żadnego sensu.
isapir
@isapir To świetna odpowiedź dla osób, które już używają guawy
nasch
1
@nasch Guava nie było częścią pytania. W każdym razie w 2018 roku naprawdę nie ma powodu, aby używać narzędzia Guava's Joiner, skoro można użyć String.join (), który został dodany w Javie 8. Zobacz stackoverflow.com/a/43011939/968244
isapir
@isapir Nie, odpowiedzią była Guava. Odpowiedzi na StackOverflow mają szerszą publiczność niż tylko osoba zadająca pytanie, więc jest w porządku, jeśli niektóre odpowiedzi nie są najlepsze dla osoby, która pyta. A komentarz dotyczący String.join () jest świetny, chociaż nie jestem pewien, czy jest dostępny we wszystkich wersjach Androida, co jest znaczącym zastosowaniem Javy.
nasch
7

Możesz użyć standardowej String.formatfunkcji do wygenerowania N przestrzeni. Na przykład:

String.format("%5c", ' ');

Tworzy ciąg z 5 spacjami.

lub

int count = 15;
String fifteenSpacebars = String.format("%" + count + "c", ' ');

Tworzy ciąg 15 spacji.

Jeśli chcesz powtórzyć inny symbol, musisz zastąpić spacje żądanym symbolem:

int count = 7;
char mySymbol = '#';
System.out.println(String.format("%" + count + "c", ' ').replaceAll("\\ ", "\\" + mySymbol));

Wynik:

#######
Alexey Gusev
źródło
6

Mój wkład oparty na algorytmie szybkiego potęgowania.

/**
 * Repeats the given {@link String} n times.
 * 
 * @param str
 *            the {@link String} to repeat.
 * @param n
 *            the repetition count.
 * @throws IllegalArgumentException
 *             when the given repetition count is smaller than zero.
 * @return the given {@link String} repeated n times.
 */
public static String repeat(String str, int n) {
    if (n < 0)
        throw new IllegalArgumentException(
                "the given repetition count is smaller than zero!");
    else if (n == 0)
        return "";
    else if (n == 1)
        return str;
    else if (n % 2 == 0) {
        String s = repeat(str, n / 2);
        return s.concat(s);
    } else
        return str.concat(repeat(str, n - 1));
}

Przetestowałem algorytm na dwóch innych podejściach:

  • Zwykła pętla for używa String.concat()do łączenia łańcucha
  • Zwykła pętla for przy użyciu pliku StringBuilder

Kod testowy (konkatenacja za pomocą pętli for i String.concat()staje się wolniejsza dla dużych n, więc pominąłem go po 5. iteracji).

/**
 * Test the string concatenation operation.
 * 
 * @param args
 */
public static void main(String[] args) {
    long startTime;
    String str = " ";

    int n = 1;
    for (int j = 0; j < 9; ++j) {
        n *= 10;
        System.out.format("Performing test with n=%d\n", n);

        startTime = System.currentTimeMillis();
        StringUtil.repeat(str, n);
        System.out
                .format("\tStringUtil.repeat() concatenation performed in    %d milliseconds\n",
                        System.currentTimeMillis() - startTime);

        if (j <5) {
            startTime = System.currentTimeMillis();
            String string = "";
            for (int i = 0; i < n; ++i)
                string = string.concat(str);
            System.out
                    .format("\tString.concat() concatenation performed in        %d milliseconds\n",
                            System.currentTimeMillis() - startTime);
        } else
            System.out
                    .format("\tString.concat() concatenation performed in        x milliseconds\n");
        startTime = System.currentTimeMillis();
        StringBuilder b = new StringBuilder();
        for (int i = 0; i < n; ++i)
            b.append(str);
        b.toString();
        System.out
                .format("\tStringBuilder.append() concatenation performed in %d milliseconds\n",
                        System.currentTimeMillis() - startTime);
    }
}

Wyniki:

Performing test with n=10
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        0 milliseconds
    StringBuilder.append() concatenation performed in 0 milliseconds
Performing test with n=100
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        1 milliseconds
    StringBuilder.append() concatenation performed in 0 milliseconds
Performing test with n=1000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        1 milliseconds
    StringBuilder.append() concatenation performed in 1 milliseconds
Performing test with n=10000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        43 milliseconds
    StringBuilder.append() concatenation performed in 5 milliseconds
Performing test with n=100000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        1579 milliseconds
    StringBuilder.append() concatenation performed in 1 milliseconds
Performing test with n=1000000
    StringUtil.repeat() concatenation performed in    0 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 10 milliseconds
Performing test with n=10000000
    StringUtil.repeat() concatenation performed in    7 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 112 milliseconds
Performing test with n=100000000
    StringUtil.repeat() concatenation performed in    80 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 1107 milliseconds
Performing test with n=1000000000
    StringUtil.repeat() concatenation performed in    1372 milliseconds
    String.concat() concatenation performed in        x milliseconds
    StringBuilder.append() concatenation performed in 12125 milliseconds

Wniosek:

  • W przypadku dużych n- użyj podejścia rekurencyjnego
  • Dla małych n- pętla ma wystarczającą prędkość
Niels Billen
źródło
4
To ciekawa realizacja. Niestety, w przeciwieństwie do twoich wniosków, jest to bardzo nieefektywne dla dużych n. Podejrzewam, że dzieje się tak z powodu wielu alokacji pamięci, które mają miejsce za każdym razem, gdy łączysz ciągi. Spróbuj napisać go jako otokę wokół metody rekurencyjnej, która pobiera StringBuilder zamiast String. Założę się, że wyniki będą znacznie lepsze.
Klitos Kyriacou
3
Jest wiele środków ostrożności, które musisz podjąć, wykonując mikro-testy porównawcze czegoś takiego i nie sądzę, abyś je stosował! Głównymi pytaniami dotyczącymi wydajności wokół tego kodu mogą być z łatwością to, czy jest on skompilowany w Hotspot, ile tworzy śmieci itp. Założę się, że wszystkie te ifstwierdzenia zepsują przewidywanie gałęzi procesora. Naprawdę należy to powtórzyć za pomocą JMH ( openjdk.java.net/projects/code-tools/jmh ), w przeciwnym razie jest to trochę bezcelowe.
Susan W
Zauważ, że ta implementacja ma złożoność O (n * log n), podczas gdy StringBuilder to O (n) :)
Leo Leontev
4

Co powiesz na to?

char[] bytes = new char[length];
Arrays.fill(bytes, ' ');
String str = new String(bytes);
Martijn Courteaux
źródło
3
To jest to samo, co ta istniejąca odpowiedź
Krease
4

Biorąc pod uwagę, że mamy:

String c = "c"; // character to repeat, for empty it would be " ";
int n = 4; // number of times to repeat
String EMPTY_STRING = ""; // empty string (can be put in utility class)

Java 8 (przy użyciu strumienia)

String resultOne = IntStream.range(0,n)
   .mapToObj(i->c).collect(Collectors.joining(EMPTY_STRING)); // cccc

Java 8 (przy użyciu nCopies)

String resultTwo = String.join(EMPTY_STRING, Collections.nCopies(n, c)); //cccc
akhil_mittal
źródło
1
Collectors.joining(EMPTY_STRING)jest odpowiednikiemCollectors.joining()
PPartisan
2

RandomStringUtils ma przepis umożliwiający utworzenie ciągu z podanego rozmiaru wejściowego. Nie mogę komentować prędkości, ale to jedna linijka.

RandomStringUtils.random(5,"\t");

tworzy wynik

\ t \ t \ t \ t \ t

najlepiej jeśli nie chcesz widzieć \ 0 w swoim kodzie.

Mulki
źródło
2

Użyj StringUtils: StringUtils.repeat (``, 10)

Icegras
źródło
1

W większości przypadków potrzebujesz tylko Ciągów o określonej długości, powiedzmy 100 spacji. Możesz przygotować tablicę ciągów, w której numer indeksu jest równy rozmiarowi wypełnionego spacjami ciągu i wyszukać ciąg, jeśli wymagana długość mieści się w granicach, lub utworzyć go na żądanie, jeśli znajduje się poza granicami.

Andreas Dolk
źródło
0

Po prostu zamień swój StringBuffer na StringBuilder . Trudno to przebić.

Jeśli twoja długość jest dużą liczbą, możesz zaimplementować bardziej wydajne (ale bardziej niezgrabne) automatyczne dodawanie, powielające długość w każdej iteracji:

 public static String dummyString(char c, int len) {
  if( len < 1 ) return "";
  StringBuilder sb = new StringBuilder(len).append(c);
  int remnant = len - sb.length();
  while(remnant  > 0) {
   if( remnant  >= sb.length() ) sb.append(sb);
   else sb.append(sb.subSequence(0, remnant));
   remnant  = len - sb.length();
  }
  return sb.toString();
 }

Możesz również wypróbować Arrays.fill()podejście (odpowiedź FrustratedWithFormsDesigner ).

leonbloy
źródło
Czy możesz nazwać niektóre zmienne więcej niż jednym znakiem?
C. Ross
0

Można wymienić StringBufferw StringBuilder(ten ostatni nie jest zsynchronizowane, mogą być szybciej w jednej aplikacji nici)

Możesz utworzyć StringBuilderinstancję raz, zamiast tworzyć ją za każdym razem, gdy jej potrzebujesz.

Coś takiego:

class BuildString {
     private final StringBuilder builder = new StringBuilder();
     public String stringOf( char c , int times ) {

         for( int i = 0 ; i < times ; i++  ) {
             builder.append( c );
         }
         String result = builder.toString();
         builder.delete( 0 , builder.length() -1 );
         return result;
      }

  }

I użyj tego w ten sposób:

 BuildString createA = new BuildString();
 String empty = createA.stringOf( ' ', 10 );

Jeśli zachowasz swoją createAzmienną jako instancję, możesz zaoszczędzić czas podczas tworzenia instancji.

Nie jest to bezpieczne dla wątków, jeśli masz wiele wątków, każdy wątek powinien mieć własną kopię.

OscarRyz
źródło
0

Aby uzyskać dobrą wydajność, połącz odpowiedzi od aznilamir i od FrustratedWithFormsDesigner

private static final String BLANKS = "                       ";
private static String getBlankLine( int length )
{
    if( length <= BLANKS.length() )
    {
        return BLANKS.substring( 0, length );
    }
    else
    {
        char[] array = new char[ length ];
        Arrays.fill( array, ' ' );
        return new String( array );
    }
}

Dostosuj rozmiar w BLANKSzależności od wymagań. Mój konkretny BLANKSciąg ma około 200 znaków.

olibre
źródło
0

Miej taką metodę. To dodaje wymagane spacje na końcu danego, Stringaby uzyskać określoną Stringdługość o określonej długości.

public static String fillSpaces (String str) {

    // the spaces string should contain spaces exceeding the max needed
    String spaces = "                                                   ";
    return str + spaces.substring(str.length());
}
Indygowiec
źródło
0

Chcesz, aby ciąg miał stały rozmiar, więc możesz albo dopełnić, albo skrócić, do tabelowania danych ...

class Playground {
    private static String fixStrSize(String s, int n) {
        return String.format("%-" + n + "s", String.format("%." + n +"s", s));
    }

    public static void main(String[ ] args) {
        System.out.println("|"+fixStrSize("Hell",8)+"|");
        System.out.println("|"+fixStrSize("Hells Bells Java Smells",8)+"|");
    }
}

|Hell    |
|Hells Be|

Doskonałe odniesienie tutaj .

JGFMK
źródło
0

Udało mi się to bez użycia zewnętrznych bibliotek w Javie 8

String sampleText = "test"
int n = 3;
String output = String.join("", Collections.nCopies(n, sampleText));
System.out.println(output);

A wynik jest

testtesttest
Sajan John
źródło
0

int c = 10; Spacje tekstowe = String.format ("%" + c + "c", ''); to rozwiąże twój problem.

Panie Geeky
źródło
-1

Można również zastosować prostą metodę, jak poniżej

public static String padString(String str, int leng,char chr) {
        for (int i = str.length(); i <= leng; i++)
            str += chr;
        return str;
    }
Yashpal Singla
źródło
Jest to zasadniczo wolniejsze.
SLaks
Możesz użyć StringBuffer zamiast konkatenacji ciągów, w przypadku, gdy ciągi, z którymi masz do czynienia, mają duży rozmiar. To znacząco wpłynie na prędkość
Yashpal Singla
-2

co powiesz na to?

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

EDYCJA: Napisałem prosty kod, aby przetestować koncepcję i oto co znalazłem.

Metoda 1: dodanie pojedynczej spacji w pętli:

  public String execLoopSingleSpace(int len){
    StringBuilder sb = new StringBuilder();

    for(int i=0; i < len; i++) {
        sb.append(' ');
    }

    return sb.toString();
  }

Metoda 2: dołącz 100 spacji i zapętl, a następnie podciąg:

  public String execLoopHundredSpaces(int len){
    StringBuilder sb = new StringBuilder("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ");

    for (int i=0; i < len/100 ; i++) {
        sb.append("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ")
            .append("          ").append("          ").append("          ");
    }

    return sb.toString().substring(0,len);
  }

Wynik otrzymuję tworząc 12 345 678 przestrzeni:

C:\docs\Projects> java FillSpace 12345678
method 1: append single spaces for 12345678 times. Time taken is **234ms**. Length of String is 12345678
method 2: append 100 spaces for 123456 times. Time taken is **141ms**. Length of String is 12345678
Process java exited with code 0

a dla 10 000 000 miejsc:

C:\docs\Projects> java FillSpace 10000000
method 1: append single spaces for 10000000 times. Time taken is **157ms**. Length of String is 10000000
method 2: append 100 spaces for 100000 times. Time taken is **109ms**. Length of String is 10000000
Process java exited with code 0

łączenie bezpośredniej alokacji i iteracji zawsze zajmuje mniej czasu, średnio 60 ms mniej przy tworzeniu dużych przestrzeni. W przypadku mniejszych rozmiarów oba wyniki są pomijalne.

Ale proszę, kontynuujcie komentowanie :-)

aznilamir
źródło
3
@ aznilamir: Hm, jak sobie poradzisz z 10 tys. przestrzeni?
Jayan
Pomysł polega na połączeniu pętli i bezpośredniego przydzielenia 100 miejsc. Oto fragmenty kodu:
aznilamir
Dlaczego używasz wielu dopisków, aby dodać 100 znaków? Dlaczego nie dodać jednego 100 znaków?
nycynik
-3

Nie znam żadnej wbudowanej metody na to, o co pytasz. Jednak w przypadku małej stałej długości, takiej jak 10, Twoja metoda powinna być dość szybka.

Wyskakuje
źródło
Gdyby tylko była mała stała długość, jak 10.
C. Ross