Effectively khi sử dụng String, StringBuilder và StringBuffer.

nguyendien

New Member
23/5/11
48
7
0
32
Hồ Chí Minh
www.facebook.com
Chúng ta lập trình java, String là một kiểu dữ liệu primitive, và hầu như rất nhiều coder sử dụng String như một kiểu "chuỗi" duy nhất mà không hề biết đến StringBuilder hay StringBuffer. Chúng khác nhau như thế nào?
String là kiểu dữ liệu immutable. Điều này có nghĩa: Nội dung của nó không thể thay đổi. Ví dụ: String a = "Nguyen Minh", Sau đó: a = a + " Dien". Khi tôi in ra thì sẽ là "Nguyen Minh Dien". Có xử lý gì bên trong? Thực ra, java đã tạo ra một đối tượng String mới và lưu Chuỗi "Nguyen Minh DIen" vào. Nếu trong chương trình của chúng ta thường xuyên dúng đến String cho việc ghép chuỗi, thì đây là một trong những vấn đề về performance (Bộ nhớ).
StringBuffer: Đây là kiều mutable. Chúng ta chỉ cần tạo ra đối tượng kiểu StringBuffer. sau đó sẽ nối nội dung tiếp theo vào đối tượng hiện tại mà không có sự tạo mới đối tượng. Những methods của kiểu này là Synchonized, nó sẽ được dùng hiệu quả trong threads. Nhưng nó sẽ chậm nếu chúng ta sử dụng trong chương trình với 1 thread.
HTML:
       StringBuffer bufer = "Nguyen Minh";
       bufer.append("Dien") -> nguyen minh dien
StringBuilder: Tương tự như StringBuffer, nhưng nó không là thread safe, tức các methods không có synchonized. Nó sẽ nhanh nhất trong 3 kiểu dữ liệu
HTML:
      StringBuilder builder = "Nguyen Minh";
      builder.append("Dien") -> nguyen minh dien
Đó là một trong những cách đơn giản để chương trình chúng ta effective hơn.
 

ThanhNhan

Active Member
30/12/10
1,666
14
38
Ðề: Effectively khi sử dụng String, StringBuilder và StringBuffer.

Cái này bác nói theo tài liệu của jdk phiên bản mấy vậy?
 

nguyendien

New Member
23/5/11
48
7
0
32
Hồ Chí Minh
www.facebook.com
Ðề: Effectively khi sử dụng String, StringBuilder và StringBuffer.

Ko nhớ nữa, nhưng tôi sử dụng từ 1.4 là có rồi. Giờ không còn thơ ngây như ngày nào nữa :))
 

DeadlyHallows

New Member
31/3/11
15
0
1
Ðề: Effectively khi sử dụng String, StringBuilder và StringBuffer.

Hình như bạn nguyendien có một chút nhầm lẫn ở đây thì phải. String không phải là kiểu dữ liệu primitive.

Các phần còn lại mình hoàn toàn đồng ý với bạn.

Bạn xem lại nhé. Thân!
 
20/5/10
144
2
18
Ðề: Effectively khi sử dụng String, StringBuilder và StringBuffer.

Trích dẫn từ nguyendien;18280 [B:
StringBuffer[/B]: Đây là kiều mutable. Chúng ta chỉ cần tạo ra đối tượng kiểu StringBuffer. sau đó sẽ nối nội dung tiếp theo vào đối tượng hiện tại mà không có sự tạo mới đối tượng. Những methods của kiểu này là Synchonized, nó sẽ được dùng hiệu quả trong threads. Nhưng nó sẽ chậm nếu chúng ta sử dụng trong chương trình với 1 thread.
Tại sao vây bạn. Vì mình dc biết ngày nay người ta khuyên dùng StringBuffer chứ thấy ít ai xài StringBuilder
 

DeadlyHallows

New Member
31/3/11
15
0
1
Ðề: Effectively khi sử dụng String, StringBuilder và StringBuffer.

Tại sao vây bạn. Vì mình dc biết ngày nay người ta khuyên dùng StringBuffer chứ thấy ít ai xài StringBuilder
Mình đọc lại tài liệu thì thấy StringBuilder được Sun khuyên dùng nhiều hơn chứ (trong trường hợp có thể sử dụng). Vì StringBuilder nhanh hơn StringBuffer.
 

dxchien

New Member
28/6/11
2
0
0
Ðề: Effectively khi sử dụng String, StringBuilder và StringBuffer.

Mình đọc thấy đâu đâu cũng viết "String là kiểu dữ liệu mà nội dung của nó không thể thay đổi" .Một câu khẳng định và không có lời giải thích. Mong các bạn có thể giúp mình diễn giải điều này, tại sao và nguyên do gì mà không thể thay đổi nội dung của biến String.
 

namndmk

New Member
20/4/10
2
0
0
Ðề: Effectively khi sử dụng String, StringBuilder và StringBuffer.

Mình đọc thấy đâu đâu cũng viết "String là kiểu dữ liệu mà nội dung của nó không thể thay đổi" .Một câu khẳng định và không có lời giải thích. Mong các bạn có thể giúp mình diễn giải điều này, tại sao và nguyên do gì mà không thể thay đổi nội dung của biến String.
Các bác ở trên đã giải thích rồi đấy thôi(imutable)
Đồng ý với bác DeadlyHallows, String không phải là một primitive data type (The 8 primitive data types are: byte, short, int, long, float, double, boolean and char)
Có điều thắc mắc, các bác cho ý kiến cái :
- Khi cộng thêm một chuỗi, giả sử
String myString = “Hello”;
myString = myString + ” World”;
-> Thực chất là tạo ra một chuỗi mới, không thể thay đổi chuỗi gốc->Chuỗi gốc vẫn có tên là myString? Chuỗi mới cũng là myString? 2 đối tượng cùng 1 tên?
Em mới tìm hiểu, xin các bác chỉ giáo!
 

Nancru

CongDongJava Project Leader
Staff member
9/10/11
1,637
310
83
Ðề: Effectively khi sử dụng String, StringBuilder và StringBuffer.

-> Thực chất là tạo ra một chuỗi mới, không thể thay đổi chuỗi gốc->Chuỗi gốc vẫn có tên là myString? Chuỗi mới cũng là myString? 2 đối tượng cùng 1 tên?
Em mới tìm hiểu, xin các bác chỉ giáo!
Đúng là tạo chuỗi mới, nhưng chuyển vùng tham chiếu của myString sang chuỗi sau
chuỗi trước ko ai tham chiếu, từ từ sẽ bị GC gom
 

xomcomon

CongdongJava Dev Group
11/12/10
711
3
18
Hà Nội
Ðề: Effectively khi sử dụng String, StringBuilder và StringBuffer.

mình nhớ là bài này post 1 lần rồi :-?
 
19/9/13
58
23
8
Mình chưa hiểu sự đồng bộ của StringBuffer và không đồng bộ của StringBuilder cho lắm, bác nào có thể cho một ví dụ minh họa để mình hiểu rõ hơn được không. Thanks nhá
p/s: sorry vì đã đào mộ :D
 

zkidkid

Member
15/1/14
74
20
8
StringBuffer:
+ sử dụng trong multi thread
+ các phương thức đều synchronized
StringBuilder:
+ sử dụng trong single thread

Do đó phụ thuộc vào tình huống bạn dùng mà sử dụng.

StringBuffer & StringBuilder chủ yếu wrapper các method từ AbstractStringBuilder (extends).
 

Thuannd

New Member
24/7/13
11
5
3
37
Mình chưa hiểu sự đồng bộ của StringBuffer và không đồng bộ của StringBuilder cho lắm, bác nào có thể cho một ví dụ minh họa để mình hiểu rõ hơn được không. Thanks nhá
p/s: sorry vì đã đào mộ :D
Cái này mình giải thích bằng một chút lý thuyết OOP nhé.

Khi method thực thi trong thực tế thì có nghĩa là xảy ra một operation (một operation có thể là tổ hợp của nhiều operation con). Operation do một thread thực hiện.

Operation không dùng đến tài nguyên toàn cục (tài nguyên của class hoặc tài nguyên của object) thì không xảy ra tranh chấp nhưng nếu dùng đến tài nguyên toàn cục thì sẽ có thể xảy ra tranh chấp. Chẳng hạn 1 cái ghế 2 ông cùng lúc nhảy vào ngồi thì sẽ có một ông không ngồi được.

Synchronized ở đây là sự kiểm soát cho phép chỉ 1 thời điểm, 1 thread thực thi operation, bắt các thread khác phải đợi. Nhớ là 1 thời điểm chỉ 1 thread thực thi operation (nó khác với concurency).

Cả StringBuilder và StringBuffer đều dùng đến global variable (ở đây là char[]) do vậy có thể có tranh chấp ở một thời điểm với nhiều thread thực thi, ví dụ có 2 ông cùng muốn gán vị trí x với các giá trị khác nhau (một ông gán 'a', một ông gán 'b') ở cùng một thời điểm. Do đó dễ làm lost data. synchronized đảm bảo 1 thời điểm chỉ có 1 phép thực thi thôi.

Trước Java 1.5, Sun chỉ giới thiệu StringBuffer, tất cả các method đều synchronized. Tuy nhiên, synchronized phải trả giá về mặt performance mà trong nhiều trường hợp thì 1 buffer cho string chỉ dùng trong context của 1 thread -> rất phí. 1.5 Sun giới thiệu thêm StringBuilder - ko synchronized các method.

Khi phân tích các trường hợp thì ta có thể dùng builder hoặc buffer cho đúng hoàn cảnh. Cái này tương tự ArrayList và vector, hashmap và hashtable.
 
  • Like
Reactions: java.technic.vn

Thuannd

New Member
24/7/13
11
5
3
37
Mình đưa thêm ví dụ về 1 hàm như sau nhé

Code:
public String toString(int [] values) {
    StringBuffer buffer = new StringBuffer();
    for(int value : values) {
      if(buffer.length() > 0) buffer.append(',');
      buffer.append(value);
    }
    return buffer.toString();
  }
Hàm này có thể đặt trong multithread nhưng viết vẫn không hợp lý vì dùng StringBuffer rất phí về mặt performance nên đổi sang StringBuilder sẽ hay hơn.

Nên nhớ tranh chấp chỉ xảy ra với tài nguyên toàn cục nhé.
 

Joe

Thành viên VIP
21/1/13
3,024
1,336
113
Well, when you talk about performance your method should be optimized, too.
PHP:
public String toString(int [] values) {
    StringBuffer buffer = new StringBuffer();
    for(int value : values) {
      //if(buffer.length() > 0) buffer.append(','); <<<superfluous!!!
      buffer.append(value+',');
    }
    return buffer.toString();
  }
 

zkidkid

Member
15/1/14
74
20
8
Hi Joe,
It's glad to see you here, I just can't help to see your next article on Difference between SD & SE.
Back to the code I think we should use StringBuilder here instead of StringBuffer.
Moreover your logic would be wrong if you use value + ',' & the result is still wrong too.
Your suggest would be:
Code:
for(int value:values){
  buffer.append(value).append(',');
}
and your result would be: 1,2,3,4,

I suggest :


Code:
public static String toString(int[] values) {
        if(values == null) return "";
        StringBuilder buffer = new StringBuilder(values.length*2);
        for(int value:values){
            buffer.append(value).append(',');
        }
        return buffer.substring(0, buffer.length()-1);

    }
 

SITUVN

Well-Known Member
25/2/12
967
263
63
Hi Joe,
It's glad to see you here, I just can't help to see your next article on Difference between SD & SE.
Back to the code I think we should use StringBuilder here instead of StringBuffer.
Moreover your logic would be wrong if you use value + ',' & the result is still wrong too.
Your suggest would be:
Code:
for(int value:values){
  buffer.append(value).append(',');
}
and your result would be: 1,2,3,4,

I suggest :


Code:
public static String toString(int[] values) {
        if(values == null) return "";
        StringBuilder buffer = new StringBuilder(values.length*2);
        for(int value:values){
            buffer.append(value).append(',');
        }
        return buffer.substring(0, buffer.length()-1);

    }
An exception (IndexOutOfBoundsException) will be throw if values.length = 0
 
  • Like
Reactions: zkidkid

Joe

Thành viên VIP
21/1/13
3,024
1,336
113
Zkidkid. Nope, the mistake I made was the typing mistake:
PHP:
public static String toString(int [] values) {
    StringBuffer buffer = new StringBuffer();
    for(int value : values) {
      //if(buffer.length() > 0) buffer.append(','); <<<superfluous!!!
      buffer.append(value+","); // <<<< double quote instead of single quote
    }
    return buffer.toString();
}
and it's correct if you want to get rid of the last comma the method can be modified as you've suggested
PHP:
   ...
    return buffer.substring(0, buffer.length()-1);
  }
And SITUVN, the check for null value is correct. It wasn't my intention to accomplish the codes, but only to pinpoint the performance problem when a loop contains superfluous statements as well as assignments and only when the author talked about performance. In this case: if(buffer.length() > 0) buffer.append(',');
 
Sửa lần cuối: