Xin vui lòng cho tôi một tình huống thời gian thực để so sánh String
, StringBuffer
và StringBuilder
?
Xin vui lòng cho tôi một tình huống thời gian thực để so sánh String
, StringBuffer
và StringBuilder
?
Câu trả lời:
Sự khác biệt về tính tương hợp:
String
là bất biến , nếu bạn cố gắng thay đổi giá trị của chúng, một đối tượng khác sẽ được tạo, trong khi đó StringBuffer
và StringBuilder
có thể thay đổi để chúng có thể thay đổi giá trị của chúng.
Sự khác biệt về an toàn của chủ đề:
Sự khác biệt giữa StringBuffer
và StringBuilder
đó StringBuffer
là an toàn chủ đề. Vì vậy, khi ứng dụng chỉ cần chạy trong một luồng duy nhất thì tốt hơn là sử dụng StringBuilder
. StringBuilder
là hiệu quả hơn StringBuffer
.
Tình huống:
String
đối tượng là bất biến.StringBuilder
là đủ tốt.StringBuffer
vì StringBuffer
nó là đồng bộ để bạn có an toàn luồng.Strings
khi chúng ta thay đổi giá trị, một đối tượng khác được tạo ra. Có phải tham chiếu đối tượng cũ bị vô hiệu hóa để nó có thể là rác được thu thập bởi GC
hoặc nó thậm chí là rác được thu thập?
String
với StringBuilder
?
String
khi một cấu trúc bất biến là phù hợp; có được một chuỗi ký tự mới từ một String
hình phạt hiệu năng không thể chấp nhận được, trong thời gian CPU hoặc bộ nhớ (có được các chuỗi con là hiệu quả của CPU vì dữ liệu không được sao chép, nhưng điều này có nghĩa là lượng dữ liệu lớn hơn nhiều có thể vẫn được phân bổ).StringBuilder
khi bạn cần tạo một chuỗi ký tự có thể thay đổi, thường là để ghép một vài chuỗi ký tự lại với nhau.StringBuffer
trong cùng một trường hợp bạn sẽ sử dụng StringBuilder
, nhưng khi thay đổi chuỗi bên dưới phải được đồng bộ hóa (vì một số luồng đang đọc / sửa đổi bộ đệm chuỗi).Xem một ví dụ ở đây .
Những thứ cơ bản:
String
là một lớp bất biến, nó không thể thay đổi.
StringBuilder
là một lớp có thể thay đổi có thể được thêm vào, các ký tự được thay thế hoặc xóa và cuối cùng được chuyển đổi thành một String
StringBuffer
là phiên bản được đồng bộ hóa ban đầu củaStringBuilder
Bạn nên ưu tiên StringBuilder
trong mọi trường hợp bạn chỉ có một luồng duy nhất truy cập vào đối tượng của mình.
Chi tiết:
Cũng lưu ý rằng đó StringBuilder/Buffers
không phải là phép thuật, họ chỉ sử dụng một Array làm đối tượng sao lưu và Array đó phải được phân bổ lại khi bao giờ nó đầy. Hãy chắc chắn và tạo các StringBuilder/Buffer
đối tượng của bạn đủ lớn ban đầu, nơi chúng không phải liên tục được định cỡ lại mỗi lần .append()
được gọi.
Việc thay đổi kích thước có thể rất thoái hóa. Về cơ bản, nó thay đổi kích thước Mảng sao lưu thành 2 lần kích thước hiện tại của nó mỗi lần nó cần được mở rộng. Điều này có thể dẫn đến một lượng lớn RAM được phân bổ và không được sử dụng khi StringBuilder/Buffer
các lớp bắt đầu phát triển lớn.
Trong Java String x = "A" + "B";
sử dụng một StringBuilder
hậu trường. Vì vậy, đối với các trường hợp đơn giản, không có lợi ích của việc khai báo của riêng bạn. Nhưng nếu bạn đang xây dựng String
các đối tượng lớn, giả sử dưới 4k, thì khai báo StringBuilder sb = StringBuilder(4096);
sẽ hiệu quả hơn nhiều so với ghép hoặc sử dụng hàm tạo mặc định chỉ có 16 ký tự. Nếu bạn String
sẽ có ít hơn 10k thì hãy khởi tạo nó với hàm tạo thành 10k để an toàn. Nhưng nếu nó được khởi tạo thành 10k thì bạn viết 1 ký tự hơn 10k, nó sẽ được phân bổ lại và sao chép vào một mảng 20k. Vì vậy, khởi tạo cao là tốt hơn so với thấp.
Trong trường hợp kích thước lại tự động, ở ký tự thứ 17, mảng sao lưu được phân bổ lại và sao chép thành 32 ký tự, ở ký tự thứ 33, điều này lại xảy ra và bạn có thể phân bổ lại và sao chép Mảng thành 64 ký tự. Bạn có thể thấy làm thế nào điều này thoái hóa thành nhiều phân bổ lại và bản sao, đó là những gì bạn thực sự đang cố gắng tránh sử dụng StringBuilder/Buffer
ở nơi đầu tiên.
Đây là từ mã nguồn JDK 6 cho AbstractStringBuilder
void expandCapacity(int minimumCapacity) {
int newCapacity = (value.length + 1) * 2;
if (newCapacity < 0) {
newCapacity = Integer.MAX_VALUE;
} else if (minimumCapacity > newCapacity) {
newCapacity = minimumCapacity;
}
value = Arrays.copyOf(value, newCapacity);
}
Một thực hành tốt nhất là khởi tạo StringBuilder/Buffer
một chút lớn hơn bạn nghĩ bạn sẽ cần nếu bạn không biết ngay lập tức String
nó sẽ lớn đến mức nào nhưng bạn có thể đoán. Một phân bổ bộ nhớ nhiều hơn một chút so với bạn cần sẽ tốt hơn nhiều phân bổ lại và bản sao.
Ngoài ra, hãy cẩn thận khi khởi tạo a StringBuilder/Buffer
với một String
cái sẽ chỉ phân bổ kích thước của Chuỗi + 16 ký tự, trong hầu hết các trường hợp sẽ chỉ bắt đầu chu trình phân bổ lại và sao chép suy biến mà bạn đang cố tránh. Dưới đây là trực tiếp từ mã nguồn Java 6.
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
Nếu bạn tình cờ kết thúc với một thể hiện StringBuilder/Buffer
mà bạn không tạo và không thể kiểm soát hàm tạo được gọi, có một cách để tránh hành vi phân bổ lại và sao chép suy biến. Gọi .ensureCapacity()
với kích thước bạn muốn để đảm bảo kết quả của bạn String
sẽ phù hợp với.
Các lựa chọn thay thế:
Cũng như một lưu ý, nếu bạn đang thực hiện việc xây dựng và thao tác thực sự nặng nề String
, có một sự thay thế định hướng hiệu suất cao hơn nhiều gọi là Ropes .
Một cách khác, là tạo ra một sự bổ sung StringList
bằng cách phân lớp phụ ArrayList<String>
và thêm các bộ đếm để theo dõi số lượng ký tự trên mỗi .append()
và các hoạt động đột biến khác của danh sách, sau đó ghi đè .toString()
để tạo StringBuilder
kích thước chính xác bạn cần và lặp qua danh sách và xây dựng đầu ra, bạn thậm chí StringBuilder
có thể biến biến đó thành một biến thể và 'bộ đệm' kết quả .toString()
và chỉ phải tạo lại nó khi có gì đó thay đổi.
Cũng đừng quên String.format()
khi xây dựng đầu ra được định dạng cố định, có thể được tối ưu hóa bởi trình biên dịch vì chúng làm cho nó tốt hơn.
String x = "A" + "B";
thực sự biên dịch để trở thành một StringBuilder? Tại sao nó không chỉ biên dịch String x = "AB";
, nó chỉ nên sử dụng StringBuilder nếu các thành phần không được biết đến vào thời gian biên dịch.
Bạn có nghĩa là, để nối?
Ví dụ về thế giới thực: Bạn muốn tạo một chuỗi mới từ nhiều người khác .
Ví dụ để gửi tin nhắn:
Chuỗi
String s = "Dear " + user.name + "<br>" +
" I saw your profile and got interested in you.<br>" +
" I'm " + user.age + "yrs. old too"
StringBuilder
String s = new StringBuilder().append.("Dear ").append( user.name ).append( "<br>" )
.append(" I saw your profile and got interested in you.<br>")
.append(" I'm " ).append( user.age ).append( "yrs. old too")
.toString()
Hoặc là
String s = new StringBuilder(100).appe..... etc. ...
// The difference is a size of 100 will be allocated upfront as fuzzy lollipop points out.
StringBuffer (cú pháp chính xác như với StringBuilder, các hiệu ứng khác nhau)
Trong khoảng
StringBuffer
so với StringBuilder
Cái trước được đồng bộ hóa và sau này thì không.
Vì vậy, nếu bạn gọi nó nhiều lần trong một luồng (chiếm 90% các trường hợp), StringBuilder
sẽ chạy nhanh hơn nhiều vì nó sẽ không dừng lại để xem liệu nó có sở hữu khóa luồng không.
Vì vậy, nên sử dụng StringBuilder
(trừ khi tất nhiên bạn có nhiều hơn một luồng truy cập vào nó cùng một lúc, điều này rất hiếm)
String
ghép nối ( sử dụng toán tử + ) có thể được trình biên dịch tối ưu hóa để sử dụng StringBuilder
bên dưới, do đó, nó không còn là điều đáng lo ngại, trong những ngày xưa của Java, đây là điều mà mọi người đều nên tránh bằng mọi giá, bởi vì mọi sự kết hợp đã tạo một đối tượng String mới. Trình biên dịch hiện đại không làm điều này nữa, nhưng vẫn là một cách tốt để sử dụng StringBuilder
thay vì chỉ trong trường hợp bạn sử dụng trình biên dịch "cũ".
biên tập
Chỉ dành cho những ai tò mò, đây là những gì trình biên dịch làm cho lớp này:
class StringConcatenation {
int x;
String literal = "Value is" + x;
String builder = new StringBuilder().append("Value is").append(x).toString();
}
javap -c StringConcatenation
Compiled from "StringConcatenation.java"
class StringConcatenation extends java.lang.Object{
int x;
java.lang.String literal;
java.lang.String builder;
StringConcatenation();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: new #2; //class java/lang/StringBuilder
8: dup
9: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V
12: ldc #4; //String Value is
14: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
17: aload_0
18: getfield #6; //Field x:I
21: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
24: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: putfield #9; //Field literal:Ljava/lang/String;
30: aload_0
31: new #2; //class java/lang/StringBuilder
34: dup
35: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V
38: ldc #4; //String Value is
40: invokevirtual #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: aload_0
44: getfield #6; //Field x:I
47: invokevirtual #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
50: invokevirtual #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
53: putfield #10; //Field builder:Ljava/lang/String;
56: return
}
Các dòng được đánh số 5 - 27 dành cho Chuỗi có tên là "bằng chữ"
Các dòng được đánh số 31-53 dành cho Chuỗi có tên "trình xây dựng"
Không có sự khác biệt, chính xác cùng một mã được thực thi cho cả hai chuỗi.
StringBuilder
phép nối Chuỗi để thực hiện ở phía bên phải của bài tập. Bất kỳ triển khai tốt nào cũng sẽ sử dụng StringBuilder đằng sau hậu trường như bạn nói. Hơn nữa, ví dụ của bạn "a" + "b"
sẽ được tổng hợp thành một nghĩa đen duy nhất "ab"
nhưng nếu bạn sử dụng StringBuilder
nó sẽ dẫn đến hai cuộc gọi không cần thiết đến append()
.
"a"+"b"
nhưng để nói phần nối chuỗi là gì về việc tôi thay đổi nó thành rõ ràng. Những gì bạn không nói là, tại sao không phải là một thực hành tốt để làm điều đó. Đó chính xác là những gì (một trình biên dịch) hiện đại làm. @fuzzy, tôi đồng ý, đặc biệt khi bạn biết kích thước của chuỗi cuối cùng sẽ là gì (aprox).
-------------------------------------------------- -------------------------------- Chuỗi StringBuffer StringBuilder -------------------------------------------------- -------------------------------- Khu vực lưu trữ | Chuỗi liên tục Heap Heap Có thể sửa đổi | Không (không thay đổi) Có (có thể thay đổi) Có (có thể thay đổi) Chủ đề an toàn | Có Có Không Hiệu suất | Nhanh Rất chậm Nhanh -------------------------------------------------- --------------------------------
synchronised
và đó là lý do .
Chuỗi
Các String class
chuỗi ký tự đại diện. Tất cả các chuỗi ký tự trong chương trình Java, chẳng hạn như "abc"
được triển khai như các thể hiện của lớp này.
Các đối tượng chuỗi là bất biến khi chúng được tạo, chúng ta không thể thay đổi. ( Chuỗi là hằng số )
Nếu một String được tạo ra sử dụng constructor hoặc phương pháp sau đó những chuỗi sẽ được lưu trữ trong bộ nhớ heap cũng như SringConstantPool
. Nhưng trước khi lưu trong pool, nó gọi intern()
phương thức để kiểm tra tính khả dụng của đối tượng với cùng một nội dung trong pool bằng phương thức equals. Nếu String-copy có sẵn trong Pool thì trả về tham chiếu. Mặt khác, đối tượng String được thêm vào nhóm và trả về tham chiếu.
+
) và để chuyển đổi các đối tượng khác thành chuỗi. Nối chuỗi được thực hiện thông qua lớp StringBuilder (hoặc StringBuffer) và phương thức nối thêm của nó.String heapSCP = new String("Yash");
heapSCP.concat(".");
heapSCP = heapSCP + "M";
heapSCP = heapSCP + 777;
// For Example: String Source Code
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
Chuỗi ký tự được lưu trữ trong StringConstantPool
.
String onlyPool = "Yash";
StringBuilder và StringBuffer là chuỗi ký tự có thể thay đổi. Điều đó có nghĩa là người ta có thể thay đổi giá trị của các đối tượng này. StringBuffer có các phương thức giống như StringBuilder, nhưng mỗi phương thức trong StringBuffer được đồng bộ hóa nên nó là luồng an toàn.
Dữ liệu StringBuffer và StringBuilder chỉ có thể được tạo bằng toán tử mới. Vì vậy, chúng được lưu trữ trong bộ nhớ Heap.
Các trường hợp của StringBuilder không an toàn để sử dụng bởi nhiều luồng. Nếu cần đồng bộ hóa như vậy thì nên sử dụng StringBuffer.
StringBuffer threadSafe = new StringBuffer("Yash");
threadSafe.append(".M");
threadSafe.toString();
StringBuilder nonSync = new StringBuilder("Yash");
nonSync.append(".M");
nonSync.toString();
StringBuffer và StringBuilder đang có một phương thức đặc biệt như.,
replace(int start, int end, String str)
Và reverse()
.
LƯU Ý : StringBuffer và SringBuilder có thể thay đổi khi chúng cung cấp việc triển khai
Appendable Interface
.
Khi nào nên dùng cái nào.
Nếu bạn sẽ không thay đổi giá trị mỗi lần thì tốt hơn nên sử dụng String Class
. Là một phần của Generics nếu bạn muốn Sắp xếp Comparable<T>
hoặc so sánh một giá trị thì hãy tìm String Class
.
//ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
Set<StringBuffer> set = new TreeSet<StringBuffer>();
set.add( threadSafe );
System.out.println("Set : "+ set);
Nếu bạn định sửa đổi giá trị mỗi lần sử dụng StringBuilder nhanh hơn StringBuffer. Nếu nhiều luồng đang sửa đổi giá trị, hãy truy cập StringBuffer.
Ngoài ra, StringBuffer
là an toàn chủ đề, mà StringBuilder
không.
Vì vậy, trong một tình huống thời gian thực khi các luồng khác nhau đang truy cập vào nó, StringBuilder
có thể có một kết quả không xác định.
Lưu ý rằng nếu bạn đang sử dụng Java 5 hoặc mới hơn, bạn nên sử dụng StringBuilder
thay vì StringBuffer
. Từ tài liệu API:
Khi phát hành JDK 5, lớp này đã được bổ sung một lớp tương đương được thiết kế để sử dụng bởi một luồng duy nhất ,
StringBuilder
. CácStringBuilder
lớp học nói chung nên được sử dụng trong ưu tiên cho một này, vì nó hỗ trợ tất cả các hoạt động tương tự, nhưng nó là nhanh hơn, vì nó thực hiện không đồng bộ.
Trong thực tế, bạn sẽ hầu như không bao giờ sử dụng điều này từ nhiều luồng cùng một lúc, vì vậy việc đồng bộ hóa StringBuffer
gần như luôn luôn là không cần thiết.
Sự khác biệt giữa String và hai lớp còn lại là String là bất biến và hai lớp còn lại là các lớp có thể thay đổi.
Nhưng tại sao chúng ta có hai lớp cho cùng một mục đích?
Lý do là đó StringBuffer
là chủ đề an toàn và StringBuilder
không.
StringBuilder
là một lớp mới StringBuffer Api
và nó đã được giới thiệu JDK5
và luôn được khuyến nghị nếu bạn đang làm việc trong một môi trường luồng đơn vì nó nhiềuFaster
Để biết chi tiết đầy đủ, bạn có thể đọc http://www.codingeek.com/java/opesbuilder-and-opesbuffer-a-way-to-create-mutable-strings-in-java/
Trong java, String là bất biến. Trở nên bất biến, chúng tôi muốn nói rằng một khi Chuỗi được tạo, chúng tôi không thể thay đổi giá trị của nó. StringBuffer có thể thay đổi. Khi một đối tượng StringBuffer được tạo, chúng ta chỉ cần nối nội dung vào giá trị của đối tượng thay vì tạo một đối tượng mới. StringBuilder tương tự StringBuffer nhưng nó không an toàn cho chủ đề. Các phương thức của StingBuilder không được đồng bộ hóa nhưng so với các Chuỗi khác, Stringbuilder chạy nhanh nhất. Bạn có thể tìm hiểu sự khác biệt giữa String, StringBuilder và StringBuffer bằng cách triển khai chúng.