java: sử dụng StringBuilder để chèn vào đầu


94

Tôi chỉ có thể làm điều này với String, ví dụ:

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

Có cách nào để đạt được điều này với StringBuilder không? Cảm ơn.

Câu trả lời:


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

Cảnh báo: Nó đánh bại mục đíchStringBuilder , nhưng nó thực hiện những gì bạn yêu cầu.


Kỹ thuật tốt hơn (mặc dù vẫn không lý tưởng):

  1. Đảo ngược từng chuỗi bạn muốn chèn.
  2. Nối mỗi chuỗi vào a StringBuilder.
  3. Đảo ngược toàn bộ StringBuilder khi bạn hoàn tất.

Điều này sẽ biến một dung dịch O ( n ²) thành O ( n ).


2
... vì nó làm cho AbstractStringBuildertất cả các nội dung di chuyển qua chỉ mục của phần chèn để tìm chỗ cho những phần được chèn. Tuy nhiên, đó là một chi tiết thực hiện, không phải là một nguyên tắc.
entonio

1
@entonio: Thật vậy, nhưng đó là một chi tiết rất quan trọng . :)
user541686

1
Tôi hiểu rồi, Có vẻ như tôi không nên sử dụng StringBuilder sau đó, Cảm ơn rất nhiều
user685275 09/05

@ user685275: Vâng, nếu bạn cần chèn ngược thì bạn thực sự cần một thứ có thể chèn vào đầu chuỗi. Tôi nghĩ giải pháp dễ nhất là kỹ thuật ở trên (đảo ngược mọi thứ hai lần), mặc dù bạn có thể tạo ra một lớp tốt hơn của riêng mình với mảng char (có thể muốn xem xét "deque").
user541686,

1
@rogerdpack: Tôi muốn nói đó là cơ chế, không phải mục đích. Mục đích là tiệm cận nhanh hơn so với thao tác chuỗi, điều này sẽ không xảy ra nếu bạn sử dụng sai.
user541686 20/02/19

32

bạn có thể dùng strbuilder.insert(0,i);


2
Tại sao điều này lại nhận được nhiều lượt thích như vậy! Lớp không được định nghĩa một cách chính xác - chỉ có chữ ký của lời gọi phương thức!
JGFMK

13

Có thể tôi đang thiếu thứ gì đó nhưng bạn muốn kết thúc bằng một Chuỗi trông như thế này "999897969594...543210", đúng không?

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

Strange bài này đã không nhận được nhiều bình chọn đồng thời cung cấp các giải pháp bằng cách thao tác khéo léo của các vòng lặp,
nôm-mon-ir

1
@ nom-mon-ir anh ấy vừa đảo ngược Chuỗi. Nó không trả lời cách nối bên trái.
Raymond Chenon

Đạt được hiệu quả mong muốn.
Speck

Tôi nghĩ rằng có thể có những trường hợp mà bạn có thể sử dụng cách tiếp cận này thay vì cố gắng hack một cách để thực hiện chèn ngay từ đầu sử dụng tiềm năng thực sự của StringBuilder. Dù sao, có những trường hợp bạn không thể đảo ngược vòng lặp, vì vậy bạn cũng cần các câu trả lời khác.
PhoneixS

7

Là một giải pháp thay thế, bạn có thể sử dụng cấu trúc LIFO (như một ngăn xếp) để lưu trữ tất cả các chuỗi và khi bạn hoàn tất, chỉ cần lấy tất cả ra và đặt chúng vào StringBuilder. Nó tự nhiên đảo ngược thứ tự của các mục (chuỗi) được đặt trong đó.

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
ArrayDequenên được sử dụng thay vì Stack. "Một tập hợp các hoạt động ngăn xếp LIFO hoàn chỉnh và nhất quán hơn được cung cấp bởi giao diện {@link Deque} và các triển khai của nó. Giao diện này sẽ được ưu tiên sử dụng cho lớp này."
Luna

5

Chủ đề này khá cũ, nhưng bạn cũng có thể nghĩ về một giải pháp đệ quy chuyển StringBuilder để lấp đầy. Điều này cho phép ngăn chặn bất kỳ quá trình xử lý ngược nào, v.v. Chỉ cần thiết kế lặp lại của bạn với một đệ quy và cẩn thận quyết định điều kiện thoát.

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

Tôi đã có một yêu cầu tương tự khi tôi tình cờ đăng bài này. Tôi muốn một cách nhanh chóng để xây dựng một Chuỗi có thể phát triển từ cả hai phía. thêm chữ mới ở mặt trước cũng như mặt sau tùy ý. Tôi biết đây là một bài đăng cũ, nhưng nó đã thôi thúc tôi thử một vài cách tạo chuỗi và tôi nghĩ mình sẽ chia sẻ những phát hiện của mình. Tôi cũng đang sử dụng một số cấu trúc Java 8 trong này, có thể đã tối ưu hóa tốc độ trong trường hợp 4 và 5.

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

Gist ở trên có mã chi tiết mà bất kỳ ai cũng có thể chạy. Tôi đã thực hiện một số cách để phát triển dây trong này; 1) Nối vào StringBuilder, 2) Chèn vào trước StringBuilder như được hiển thị bởi @Mehrdad, 3) Chèn một phần từ trước cũng như cuối StringBuilder, 4) Sử dụng danh sách để nối từ cuối, 5) Sử dụng Deque vào nối từ phía trước.

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

Tôi sẽ tập trung vào các trường hợp chỉ nối thêm phía trước, tức là. trường hợp 2 và trường hợp 5. Việc thực thi StringBuilder nội bộ quyết định cách bộ đệm bên trong phát triển, điều này ngoài việc di chuyển tất cả bộ đệm từ trái sang phải trong trường hợp bổ sung phía trước sẽ hạn chế tốc độ. Mặc dù thời gian thực hiện khi chèn trực tiếp vào phía trước của StringBuilder tăng lên đến các giá trị thực sự cao, như được hiển thị bởi @Mehrdad, nếu nhu cầu là chỉ có các chuỗi có độ dài dưới 90k ký tự (vẫn còn nhiều), thì phần chèn phía trước sẽ xây dựng một Chuỗi trong cùng một thời gian vì nó sẽ cần để tạo một Chuỗi có cùng độ dài bằng cách thêm vào ở cuối. Những gì tôi đang nói là quả phạt thời gian thực sự phát huy tác dụng và rất lớn, nhưng chỉ khi bạn phải xây dựng một chuỗi thực sự lớn. Người ta có thể sử dụng một deque và nối các chuỗi ở cuối như trong ví dụ của tôi.

Trên thực tế, hiệu suất cho trường hợp 2 nhanh hơn nhiều so với trường hợp 1, mà tôi dường như không hiểu. Tôi giả định rằng sự tăng trưởng cho bộ đệm nội bộ trong StringBuilder sẽ giống nhau trong trường hợp phần phụ trước và phần phụ sau. Tôi thậm chí còn đặt heap tối thiểu thành một số lượng rất lớn để tránh sự chậm trễ trong tăng trưởng heap, nếu điều đó có thể đóng vai trò quan trọng. Có thể ai hiểu rõ hơn có thể comment bên dưới.


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
Chào mừng bạn đến với stackoverflow. Vui lòng bao gồm giải thích về chức năng của mã và cách nó giải quyết vấn đề trong câu hỏi.
bad_coder

1

Bạn có thể sử dụng phương pháp chèn với phần bù. như offset được đặt thành '0' có nghĩa là bạn đang thêm vào phía trước StringBuilder của mình.

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

LƯU Ý : vì phương thức chèn chấp nhận tất cả các loại nguyên thủy, bạn có thể sử dụng cho int, long, char [], v.v.


0

Làm thế nào về:

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

HOẶC LÀ

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

Nhưng với điều này, bạn đang thực hiện phép toán O (N ^ 2) thay vì O (N).

Đoạn mã từ tài liệu java:

Chèn biểu diễn chuỗi của đối số Đối tượng vào chuỗi ký tự này. Hiệu ứng tổng thể chính xác như thể đối số thứ hai được chuyển đổi thành một chuỗi bằng phương thức String.valueOf(Object)và các ký tự của chuỗi đó sau đó được chèn vào chuỗi ký tự này tại độ lệch được chỉ định.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.