java: użyj StringBuilder, aby wstawić na początku

96

Mogłem to zrobić tylko ze Stringiem, na przykład:

String str="";
for(int i=0;i<100;i++){
    str=i+str;
}

Czy istnieje sposób na osiągnięcie tego za pomocą StringBuilder? Dzięki.

user685275
źródło

Odpowiedzi:

193
StringBuilder sb = new StringBuilder();
for(int i=0;i<100;i++){
    sb.insert(0, Integer.toString(i));
}

Ostrzeżenie: pokonuje celStringBuilder , ale robi to, o co prosiłeś.


Lepsza technika (choć nadal nie jest idealna):

  1. Odwróć każdy ciąg, który chcesz wstawić.
  2. Dołącz każdy ciąg do pliku StringBuilder.
  3. Odwrócenie całej karty StringBuilder , kiedy skończysz.

To zamieni rozwiązanie O ( n ²) w O ( n ).

user541686
źródło
2
... ponieważ powoduje AbstractStringBuilderprzeniesienie całej zawartości poza indeks wstawiania, aby znaleźć miejsce na wstawione. Jest to jednak szczegół implementacji, a nie zasada.
entonio
1
@entonio: Rzeczywiście, ale to bardzo krytyczny szczegół. :)
user541686
1
Widzę, wygląda na to, że nie powinienem wtedy używać StringBuilder, wielkie dzięki
user685275
@ user685275: Tak, jeśli potrzebujesz wstawiania wstecz, naprawdę potrzebujesz czegoś, co można wstawić na początku ciągu. Myślę, że najłatwiejszym rozwiązaniem jest powyższa technika (dwukrotne odwrócenie rzeczy), chociaż prawdopodobnie mógłbyś stworzyć lepszą własną klasę za pomocą tablic char (warto zajrzeć do "deque").
user541686
1
@rogerdpack: Powiedziałbym, że to mechanizm, a nie cel. Celem jest asymptotycznie szybsze działanie niż manipulacja ciągami znaków, co nie będzie możliwe, jeśli użyjesz go nieprawidłowo.
user541686
32

możesz użyć strbuilder.insert(0,i);

maniak grzechotki
źródło
2
Dlaczego zyskało tak wiele polubień! Klasa nie jest poprawnie zdefiniowana - tylko sygnatura wywołania metody!
JGFMK
14

Może czegoś mi brakuje, ale chcesz skończyć ze stringiem, który wygląda tak "999897969594...543210", prawda?

StringBuilder sb = new StringBuilder();
for(int i=99;i>=0;i--){
    sb.append(String.valueOf(i));
}
Plamka
źródło
Dziwne, że ten post nie uzyskał zbyt wielu głosów, zapewniając rozwiązanie przez sprytną manipulację pętlą,
nom-mon-ir
1
@ nom-mon-ir on właśnie odwraca String. Nie odpowiada, jak dołączyć po lewej stronie.
Raymond Chenon
Osiąga pożądany efekt.
Speck
Myślę, że mogą istnieć przypadki, w których można użyć tego podejścia zamiast próbować zhakować sposób wykonania wstawki na początku, który wykorzystuje prawdziwy potencjał StringBuilder. W każdym razie są przypadki, w których nie można odwrócić pętli, więc potrzebujesz również innych odpowiedzi.
PhoneixS,
7

Jako alternatywne rozwiązanie możesz użyć struktury LIFO (takiej jak stos) do przechowywania wszystkich ciągów, a kiedy skończysz, po prostu wyjmij je wszystkie i umieść w StringBuilder. W naturalny sposób odwraca kolejność umieszczonych w nim elementów (ciągów znaków).

Stack<String> textStack = new Stack<String>();
// push the strings to the stack
while(!isReadingTextDone()) {
    String text = readText();
    textStack.push(text);
}
// pop the strings and add to the text builder
String builder = new StringBuilder(); 
while (!textStack.empty()) {
      builder.append(textStack.pop());
}
// get the final string
String finalText =  builder.toString();
Vasile Jureschi
źródło
4
ArrayDequepowinien być używany zamiast Stack. „Bardziej kompletny i spójny zestaw operacji na stosie LIFO zapewnia interfejs {@link Deque} i jego implementacje, które powinny być używane zamiast tej klasy.”
Luna
5

Ten wątek jest dość stary, ale możesz również pomyśleć o rozwiązaniu rekurencyjnym przekazującym StringBuilder do wypełnienia. Pozwala to zapobiec wszelkiemu odwrotnemu przetwarzaniu itp. Wystarczy zaprojektować iterację z rekurencją i dokładnie zdecydować o warunku wyjścia.

public class Test {

    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder();
        doRecursive(sb, 100, 0);
        System.out.println(sb.toString());
    }

    public static void doRecursive(StringBuilder sb, int limit, int index) {
        if (index < limit) {
            doRecursive(sb, limit, index + 1);
            sb.append(Integer.toString(index));
        }
    }
}
Benzoes
źródło
3

Miałem podobny wymóg, kiedy natknąłem się na ten post. Chciałem szybko zbudować String, który może rosnąć z obu stron, tj. dodaj dowolnie nowe litery z przodu iz tyłu. Wiem, że to stary post, ale zainspirował mnie do wypróbowania kilku sposobów tworzenia ciągów i pomyślałem, że podzielę się swoimi odkryciami. Używam również niektórych konstrukcji Java 8, które mogły zoptymalizować prędkość w przypadkach 4 i 5.

https://gist.github.com/SidWagz/e41e836dec65ff24f78afdf8669e6420

Powyższe streszczenie zawiera szczegółowy kod, który każdy może uruchomić. Zrobiłem w tym kilka sposobów na zapuszczenie sznurków; 1) Dołącz do StringBuilder, 2) Wstaw z przodu StringBuilder, jak pokazano na @Mehrdad, 3) Częściowo wstaw od przodu i od końca StringBuilder, 4) Użyj listy do dołączenia od końca, 5) Użyj Deque do dołączać z przodu.

// Case 2    
StringBuilder build3 = new StringBuilder();
IntStream.range(0, MAX_STR)
                    .sequential()
                    .forEach(i -> {
                        if (i%2 == 0) build3.append(Integer.toString(i)); else build3.insert(0, Integer.toString(i));
                    });
String build3Out = build3.toString();


//Case 5
Deque<String> deque = new ArrayDeque<>();
IntStream.range(0, MAX_STR)
                .sequential()
                .forEach(i -> {
                    if (i%2 == 0) deque.addLast(Integer.toString(i)); else deque.addFirst(Integer.toString(i));
                });

String dequeOut = deque.stream().collect(Collectors.joining(""));

Skoncentruję się na przedniej dołączanej tylko przypadkach, tj. Przypadek 2 i przypadek 5. Implementacja StringBuildera wewnętrznie decyduje o wzroście bufora wewnętrznego, który poza przesunięciem całego bufora od lewej do prawej w przypadku dołączania z przodu ogranicza prędkość. Podczas gdy czas potrzebny przy wstawianiu bezpośrednio na przód StringBuilder rośnie do naprawdę wysokich wartości, jak pokazano w @Mehrdad, jeśli potrzeba ma tylko ciągi o długości mniejszej niż 90 tysięcy znaków (co nadal jest dużo), przednia wstawka będzie zbudować String w tym samym czasie, jaki zajęłoby zbudowanie String o tej samej długości przez dołączenie na końcu. Chodzi mi o to, że kara czasowa rzeczywiście kopie i jest ogromna, ale tylko wtedy, gdy trzeba zbudować naprawdę ogromne struny. Można by użyć deque i połączyć struny na końcu, jak pokazano w moim przykładzie.

W rzeczywistości wydajność w przypadku 2 jest znacznie szybsza niż w przypadku 1, którego nie rozumiem. Zakładam, że wzrost wewnętrznego bufora w StringBuilder byłby taki sam w przypadku dołączania z przodu iz tyłu. Ustawiłem nawet minimalną stertę na bardzo dużą wartość, aby uniknąć opóźnienia wzrostu sterty, gdyby odegrało to jakąś rolę. Może ktoś, kto ma lepsze zrozumienie, może skomentować poniżej.

Siddharth Wagle
źródło
1
Difference Between String, StringBuilder And StringBuffer Classes
String
String is immutable ( once created can not be changed )object. The object created as a
String is stored in the Constant String Pool.
Every immutable object in Java is thread-safe, which implies String is also thread-safe. String
can not be used by two threads simultaneously.
String once assigned can not be changed.
StringBuffer
StringBuffer is mutable means one can change the value of the object. The object created
through StringBuffer is stored in the heap. StringBuffer has the same methods as the
StringBuilder , but each method in StringBuffer is synchronized that is StringBuffer is thread
safe .
Due to this, it does not allow two threads to simultaneously access the same method. Each
method can be accessed by one thread at a time.
But being thread-safe has disadvantages too as the performance of the StringBuffer hits due
to thread-safe property. Thus StringBuilder is faster than the StringBuffer when calling the
same methods of each class.
String Buffer can be converted to the string by using
toString() method.

    StringBuffer demo1 = new StringBuffer("Hello") ;

// The above object stored in heap and its value can be changed.
/
// Above statement is right as it modifies the value which is allowed in the StringBuffer
StringBuilder
StringBuilder is the same as the StringBuffer, that is it stores the object in heap and it can also
be modified. The main difference between the StringBuffer and StringBuilder is
that StringBuilder is also not thread-safe.
StringBuilder is fast as it is not thread-safe.
/
// The above object is stored in the heap and its value can be modified
/
// Above statement is right as it modifies the value which is allowed in the StringBuilder
suresh babu garine
źródło
1
Witamy w stackoverflow. Dołącz wyjaśnienie, co robi kod i jak rozwiązuje problem w pytaniu.
bad_coder
1

Możesz użyć metody wstawiania z odsunięciem. as offset ustawiony na „0” oznacza, że ​​dołączasz do początku swojego StringBuildera.

StringBuilder sb = new StringBuilder();
for(int i=0;i<100;i++){
    sb.insert(0,i);
}

UWAGA : ponieważ metoda insert akceptuje wszystkie typy prymitywów, możesz użyć int, long, char [] itp.

sachyy
źródło
0

Co powiesz na:

StringBuilder builder = new StringBuilder();
for(int i=99;i>=0;i--){
    builder.append(Integer.toString(i));
}
builder.toString();

LUB

StringBuilder builder = new StringBuilder();
for(int i=0;i<100;i++){
  builder.insert(0, Integer.toString(i));
}
builder.toString();

Ale w ten sposób wykonujesz operację O (N ^ 2) zamiast O (N).

Fragment z dokumentów java:

Wstawia ciąg reprezentujący argument Object do tej sekwencji znaków. Ogólny efekt jest dokładnie taki, jakby drugi argument został przekonwertowany na łańcuch za pomocą metody String.valueOf(Object), a znaki tego ciągu zostały następnie wstawione do tej sekwencji znaków we wskazanym przesunięciu.

Próbować
źródło