java: використовуйте StringBuilder для вставки на початку


94

Я міг зробити це лише за допомогою рядка, наприклад:

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

Чи є спосіб досягти цього за допомогою StringBuilder? Дякую.

Відповіді:


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

Попередження: це не відповідає метіStringBuilder , але виконує те, що ви просили.


Краща техніка (хоча все ще не ідеальна):

  1. Змініть кожен рядок, який потрібно вставити.
  2. Додайте кожен рядок до a StringBuilder.
  3. Поверніть все, StringBuilder коли закінчите.

Це перетворить розчин O ( n ²) в O ( n ).


2
... оскільки він змушує AbstractStringBuilderпереміщати весь вміст повз індекс вставки, щоб знайти місце для вставлених. Однак це деталь реалізації, а не одна з принципових.
entonio

1
@entonio: Дійсно, але це дуже важлива деталь. :)
user541686

1
Бачу, схоже, тоді я не повинен був використовувати StringBuilder, велике
спасибі

@ user685275: Так, якщо вам потрібна вставка назад, тоді вам дійсно потрібно щось, що можна вставити на початку рядка. Я думаю, що найпростішим рішенням є наведена вище методика (двічі змінити ситуацію назад), хоча ви, мабуть, можете зробити власний кращий клас за допомогою масивів char (можливо, захочете розглянути "деке").
user541686

1
@rogerdpack: Я б сказав, що це механізм, а не мета. Мета полягає в тому, щоб бути асимптотично швидшими, ніж маніпулювання рядками, чого не буде, якщо ви використаєте його неправильно.
user541686

31

ви можете використовувати strbuilder.insert(0,i);


2
Чому це набрало стільки лайків! Клас визначений неправильно - лише підпис виклику методу!
JGFMK

13

Можливо, я чогось пропустив, але ти хочеш закінчити рядком, який виглядає так "999897969594...543210", вірно?

StringBuilder sb = new StringBuilder();
for(int i=99;i>=0;i--){
    sb.append(String.valueOf(i));
}

Дивно, але ця публікація не набрала багато голосів, забезпечуючи рішення шляхом розумних маніпуляцій із петлею,
Ном-Пн-Ір,

1
@ nom-mon-ir він просто перевертає струну назад. Це не відповідає, як додати ліворуч.
Реймонд Ченон,

Досягає бажаного ефекту.
Спек

Я думаю, що можуть бути випадки, коли ви можете використовувати цей підхід, замість того, щоб намагатися зламати спосіб вставки на початку, який використовує справжній потенціал StringBuilder. У будь-якому випадку, бувають випадки, коли ви не можете змінити цикл, тому вам потрібні й інші відповіді.
PhoneixS

7

Як альтернативне рішення ви можете використовувати структуру LIFO (наприклад, стек) для зберігання всіх рядків, а коли закінчите, просто вийміть їх і помістіть у StringBuilder. Це, природно, змінює порядок розміщення в ньому елементів (рядків).

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

4
ArrayDequeслід використовувати замість Stack. "Більш повний та послідовний набір операцій стеку LIFO забезпечується інтерфейсом {@link Deque} та його реалізаціями, які слід використовувати переважно перед цим класом."
Луна

5

Цей потік досить старий, але ви також можете подумати про рекурсивне рішення, яке передає StringBuilder для заповнення. Це дозволяє запобігти будь-якій зворотній обробці і т. Д. Просто потрібно розробити свою ітерацію з рекурсією і ретельно визначитися з умовою виходу.

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

3

У мене була подібна вимога, коли я натрапив на цю посаду. Я хотів швидкий спосіб побудувати рядок, який може рости з обох сторін, тобто. додавайте нові літери як спереду, так і ззаду довільно. Я знаю, що це стара публікація, але вона надихнула мене випробувати кілька способів створення рядків, і я подумав поділитися своїми висновками. Я також використовую в цьому деякі конструкції Java 8, які могли б оптимізувати швидкість у випадках 4 і 5.

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

У суті вище є детальний код, який може запустити кожен. Я взяв кілька способів вирощування струн у цьому; 1) Додайте до StringBuilder, 2) Вставте перед StringBuilder, як показано @Mehrdad, 3) Частково вставте як спереду, так і з кінця StringBuilder, 4) Використовуючи список для додавання з кінця, 5) Використання Deque до додайте спереду.

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

Я зупинюсь на передній частині додатка, лише регістри, тобто. випадок 2 та випадок 5. Реалізація StringBuilder внутрішньо вирішує, як зростати внутрішній буфер, який, крім переміщення всього буфера зліва направо у випадку додавання фронту, обмежує швидкість. Хоча час, необхідний для вставки безпосередньо в передню частину StringBuilder, зростає до справді високих значень, як показує @Mehrdad, якщо потрібно мати лише рядки довжиною менше 90 тис. Символів (що все ще багато), передня вставка буде побудувати рядок у той самий час, скільки потрібно для побудови рядка однакової довжини, додавши його в кінці. Я хочу сказати, що покарання за часом, справді, дуже велике, але лише тоді, коли вам потрібно будувати справді величезні струни. Можна використовувати deque і об’єднати рядки в кінці, як показано в моєму прикладі.

Насправді продуктивність у випадку 2 набагато швидша, ніж у випадку 1, чого я, здається, не розумію. Я припускаю, що зростання внутрішнього буфера в StringBuilder буде однаковим у випадку переднього та заднього додавання. Я навіть встановив мінімальну купу дуже великої суми, щоб уникнути затримки в зростанні купи, якби це зіграло свою роль. Можливо, хтось, хто краще розуміється, може прокоментувати нижче.


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

1
Ласкаво просимо до stackoverflow. Будь ласка, включіть пояснення до того, що робить код і як він вирішує проблему, у питанні.
bad_coder

1

Ви можете використовувати метод вставки зі зміщенням. оскільки зміщення встановлено на '0' означає, що ви додаєте до передньої частини вашого StringBuilder.

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

ПРИМІТКА : оскільки метод вставки приймає всі типи примітивів, ви можете використовувати для int, long, char [] тощо.


0

Як на рахунок:

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

АБО

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

Але цим ви робите операцію O (N ^ 2) замість O (N).

Фрагмент з Java-документів:

Вставляє рядкове представлення аргументу Object у цю послідовність символів. Загальний ефект точно такий же, як ніби другий аргумент був перетворений методом у рядок String.valueOf(Object), а символи цього рядка потім були вставлені в цю послідовність символів із зазначеним зміщенням.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.