Giới thiệu Trong Java, việc xử lý chuỗi trở nên rất nhẹ nhàng do các công việc liên quan đều đã được cài đặt sẵn trong JDK, thông qua các lớp String, StringBuffer và String: là lớp đơn
Trang 1Strings 1
1 Giới thiệu 1
2 Lớp String 1
3 Lớp StringBuffer và StringBuilder 8
4 StringBuffer hay String 10
1 Giới thiệu
Trong Java, việc xử lý chuỗi trở nên rất nhẹ nhàng do các công việc liên quan đều đã được cài đặt sẵn trong JDK, thông qua các lớp String, StringBuffer và
String: là lớp đơn giản nhất, có đặc tính là không thể thay đổi nên tiết kiệm bộ nhớ, xử lý nhanh và có thể dùng chung ở nhiều nơi khác nhau
có thể thay đổi các ký tự trong đó Thêm vào đó lớp này còn có tính năng đồng bộ hóa nên có thể được dùng trong môi trường đa tiểu trình Tuy nhiên, do có nhiều tính năng mạnh nên StringBuffer cồng kềnh và chậm chạp hơn String
năng đồng bộ hóa nên thích hợp cho môi trường đơn tiểu trình
Mỗi lớp đều có điểm mạnh và điểm yếu riêng, nên tùy trường hợp mà ta chọn lớp phù hợp: nếu chỉ cần lưu chuỗi mà ít xử lý thì chọn String, cần thay đổi chuỗi nhiều thì dùng
StringBuilder, nếu dùng trong môi trường đa tiểu trình thì dùng StringBuffer
2 Lớp String
2.1 Giới thiệu
Trong Java lớp String (package java.lang) được dùng để biểu diễn các chuỗi ký tự Tất
cả các dạng literal như "hello", "abc", đều là những đối tượng thuộc lớp String
Các đối tượng String đều là hằng, có nghĩa là ta không thể thay đổi giá trị của chúng sau khi chúng được tạo ra, do đó ta có thể dùng một String ở nhiều nơi khác nhau Để "thay đổi" một String, ta chỉ có cách tạo ra String mới dựa vào String gốc, rồi dùng String
mới này thay cho String cũ Điều này sẽ dẫn tới việc phát sinh ra nhiều "rác" và tốn nhiều thời gian xử lý nếu ta thường xuyên "thay đổi" các String
String là một final class nên ta không thể tạo ra các lớp con từ nó
public final class String
extends Object
implements Serializable, Comparable<String>, CharSequence
Trang 22.2 Tạo ra một String
Để tạo ra một đối tượng String, có nhiều cách:
2.2.1 Dùng string literal
Đối tượng String chứa literal sẽ được tạo ra trong constant pool
Ví dụ:
String s= "abc" ;
Khi đó, nếu ta tạo ra một chuỗi s2 mới:
String s2= "abc" ;
thì s2 sẽ quản lý cùng một chuỗi "abc" như s, (chỉ có handle mới là s2, không có đối tượng String mới nào được tạo ra), do đó phép so sánh (s==s2) sẽ trả về true
Lưu ý:
String litertal cũng là các đối tượng, nên ta có thể gọi các phương thức từ chúng
2.2.2 Dùng constructor
Đối tượng String này sẽ nằm trong heap
chuỗi null
hiện hành của hệ thống
Tạo ra chuỗi từ một phần của mảng byte, bắt đầu từ byte thứ offset trong mảng, dùng tất cả length
byte, giải mã theo bộ ký tự hiện hành của hệ thống
Tạo ra chuỗi từ một phần của mảng ký tự, bắt đầu
từ ký tự thứ offset trong mảng, độ dài tổng cộng
là length
chép mảng ký tự của StringBuffer
Bảng 1: Một số constructor của String
Ví dụ:
char data[]={ 'a' , 'b' , 'c' };
Trang 3String s2=new String( "Hello" );
Khi đó, nếu ta tạo ra một chuỗi s3 mới:
String s3=new String( "Hello" );
thì phép so sánh s2 == s3 sẽ trả về false Muốn so sánh dữ liệu của chúng, ta phải dùng phương thức equals()
2.3 Kết nối các String
Trong Java, ta có thể ghép nối tiếp các String một cách dễ dàng bằng toán tử "+"
String s1= "Hello" ;
String s2= "Java" ;
Thêm vào đó, ta cũng có thể ghép nối một đối tượng bất kỳ vào String cũng bằng toán
tử + này Khi đó, đối tượng sẽ được tự biến thành chuỗi bằng phương thức toString(), sau đó chuỗi kết quả mới được ghép vào như trường hợp ghép hai chuỗi
Cộng hai biến int vào chuỗi:
Cộng hai biến int vào chuỗi, dùng dấu ngoặc để tính tổng các số nguyên trước:
String s= "Hello" +(i+j); // s sẽ là "Hello300"
Ngoài cách dùng toán tử +, ta cũng có thể dùng phương thức concat()
String s1= "Hello" ;
String s2= "Java" ;
Lưu ý:
Nếu đối số của phương thức concat() là chuỗi rỗng (length = 0), kết quả trả về chính là chuỗi gọi phương thức (this)
String s1= "abc" ;
String s2= "" ;
String s3=s1.concat(s2);// s3 sẽ là "abc"
System.out.println( "Bang nhau" );
Nếu đối số của phương thức concat() là chuỗi khác rỗng, một String mới (là
"tổng" của this và đối số) sẽ được tạo ra
Trang 4 Xét phép nối chuỗi sau:
String s= "Java" ;
s+= "String" ; // s sẽ là "JavaString"
phép nối chuỗi này thoạt nhìn thì giống như là thêm đọan "String" vào chuỗi "Java", nhưng thực ra không phải như vậy Trong Java, String là đối tượng không thể thay đổi nên không thể nối thêm một phần vào chuỗi "Java" được Bản chất của lệnh đó là tạo ra một chuỗi mới có giá trị "JavaString", sau đó cho handle s quản lý chuỗi mới đó, chuỗi
"String" cũ không hề thay đổi và sẽ tồn tại nếu vẫn còn handle nào đó quản lý nó
2.4 Trích ký tự từ String
Ta có thể lấy ra từng ký tự, mảng ký tự hoặc mảng byte từ dãy ký tự của String bằng các phương thức sau:
int codePointAt(int index) Trả về mã Unicode của ký tự ở vị trí index
mặc định của hệ thống
void getChars(int srcBegin,
dstBegin)
Trích một phần của chuỗi ra một mảng ký tự, từ ký
tự ở vị trí srcBegin tới ký tự ở vị trí srcEnd, lưu vào mảng kể từ phần tử thứ dstBegin
Bảng 2: Một số phương thức trích ký tự từ String
Ví dụ:
Trích ra từng ký tự:
String s= "Hello Java" ;
System.out.print( "Chuoi dao nguoc cua " +s+ " la: " );
System.out.print(s.charAt(i));
System.out.println();
kết quả:
Chuoi dao nguoc cua Hello Java la: avaJ olleH
Press any key to continue
Trích ra mảng ký tự:
String s= "String" ;
System.out.println( "Ky tu thu " +i+ " la: " +chars[i]);
kết quả:
Ky tu thu 0 la: S
Trang 5Ky tu thu 1 la: t
Ky tu thu 2 la: r
Ky tu thu 3 la: i
Ky tu thu 4 la: n
Ky tu thu 5 la: g
Press any key to continue
Trích ra mảng byte:
String s= "ABCabc" ;
System.out.println( "Byte thu " +i+ " la: " +bytes[i]);
kết quả:
Byte thu 0 la: 65
Byte thu 1 la: 66
Byte thu 2 la: 67
Byte thu 3 la: 97
Byte thu 4 la: 98
Byte thu 5 la: 99
Press any key to continue
2.5 So sánh và tìm kiếm trên String
Java cung cấp sẵn các phương thức sau:
So sánh dãy ký tự của str với this theo giá trị của từng ký tự trong bảng mã (VD: 'a' < 'b', 'F' < 'f',
'Z' < 'a', ) Giá trị trả về sẽ là độ chênh lệch về
mã giữa hai ký tự khác nhau đầu tiên trong hai dãy, tính từ trái sang phải
Do đó:
Trả về 0 nếu hai dãy ký tự giống nhau
Trả về < 0 nếu dãy của this nhỏ hơn str
Trả về > 0 nếu dãy của this lớn hơn str
(String str)
Tương tự compareTo(String) nhưng không phân biệt chữ HOA với chữ thường ('A' và 'a' là như nhau)
boolean
contains(CharSequence s)
Trả về true nếu trong chuỗi this có xuất hiện dãy
ký tự s
boolean equals(Object o) So sánh chuỗi với đối tượng khác
boolean equalsIgnoreCase
(String str)
So sánh chuỗi với chuỗi khác, không phân biệt HOA thường
Trang 6int indexOf(char c)
Cho biết vị trí xuất hiện đầu tiên của ký tự c trong chuỗi this
Quy ước: trả về -1 nếu trong this không có c
int indexOf(char c, int
from)
Cho biết vị trí xuất hiện đầu tiên của ký tự c trong chuỗi this, tính từ vị trí from trở về sau
Quy ước: trả về -1 nếu kể từ vị trí from, trong this
không có c
int indexOf(String str)
Cho biết vị trí xuất hiện đầu tiên của chuỗi con str
trong chuỗi this Quy ước: trả về -1 nếu trong this không có str
int indexOf(String str, int
from)
Cho biết vị trí xuất hiện đầu tiên của chuỗi con str
trong chuỗi this, tính từ vị trí from trở về sau Quy ước: trả về -1 nếu kể từ vị trí from, trong this
không có str
Cho biết vị trí xuất hiện sau cùng của ký tự c trong chuỗi this
Quy ước: trả về -1 nếu trong this không có c Các phương thức lastIndexOf(char, int),
int) cũng tương tự
Bảng 3: Các phương thức so sánh và tìm kiếm trên String
Ví dụ:
String s= "Java Strings" ;
2.6 Mộ số phương thức khác
Java cung cấp các phương thức sau:
Trang 7String replace(char old,
Trả về chuỗi mới từ chuỗi this, thay tất cả các ký
tự old bởi ký tự new
String replace(CharSequence
old, CharSequence new)
Trả về chuỗi mới từ chuỗi this, thay tất cả các dãy
ký tự old bởi dãy ký tự new
String replaceAll(String
regex, String new)
Trả về chuỗi mới từ chuỗi this, thay tất cả các dãy
ký tự thỏa biểu thức chính quy regex old bởi dãy
ký tự new Chi tiết về biểu thức chính quy (regular expression) xin xem thêm trong Java Documents
String replaceFirst(String
regex, String new)
Tương tự replaceAll nhưng chỉ thay thế dãy ký
tự đầu tiên thỏa biểu thức chính quy
không có khoảng trắng đầu và cuối
Bảng 4: Một số phương thức khác của String
Và còn nhiều phương thức nữa, chi tiết xin xem trong Java API Documents
Ví dụ:
String s= "Java Strings" ;
System.out.println(s.replaceAll( "a" , "x" )); // Jxvx Strings
System.out.println(s.replaceAll( "a|i" , "x" )); // Jxvx Strxngs
System.out.println( " Spaces " trim()); // "spaces"
"boo:and:foo" split( ":" )); // {"boo", "and", "foo"}
"boo:and:foo" split( "o" ); // {"b", "", ":and:f"}
"boo:and:foo" split( "f|a" ); // {"boo:", "nd:", "oo"}
String s= "chduy@hcmuns.edu.vn";
s.split( "@|\\." ); // {"chduy", "hcmuns", "edu", "vn"}
Nhắc lại:
Trang 8 Trong tất cả phương thức chuỗi gốc this không bị thay đổi, chuỗi kết quả do phương thức trả về là một chuỗi mới hoàn toàn
3 Lớp StringBuffer và StringBuilder
3.1 Giới thiệu
3.1.1 Lớp StringBuffer
StringBuffer có thể xem như lớp "nâng cấp" của String do StringBuffer có đủ các tính năng của String và có thêm tính năng thay đổi (thêm, xóa, sửa, thay thế, ) các ký tự bên trong nó
Do có tính năng đồng bộ hóa, StringBuffer có thể được dùng chung trong nhiều thread Tương tự như String, StringBuffer cũng là lớp final
public final class StringBuffer
extends Object
implements Serializable, CharSequence
Bên cạch độ dài (length) là số ký tự đang dùng, StringBuffer có một thông số khác là sức chứa (capacity), đó là số ký tự tối đa nó có thể chứa mà không cần xin thêm bộ nhớ
Do đó, length không thể vượt quá capacity
Nếu ta ghép hoặc chèn vào StringBuffer một chuỗi mới, nếu capatity vẫn đủ chứa phần chuỗi ban đầu và phần chuỗi mới thì StringBuffer không cần xin cấp bộ nhớ nên tốc độ
sẽ nhanh hơn Nếu capatity không đủ thì StringBuffer sẽ tự động xin thêm bộ nhớ Trong thao tác cộng của String, StringBuffer đóng vai trò "làm việc sau hậu trường"
Ví dụ:
đoạn code sau
String s= "X" + 4 + "Y" ;
sẽ được chuyển thành
String s =
new StringBuffer().append( "X" ).append(4).append( "Y" ).toString();
3.1.2 Lớp StringBuilder
Nhìn chung lớp này có các phương thức tương tự như StringBuffer, nhưng không có tính năng đồng bộ hóa Lớp StringBuilder được bổ sung vào JDK 1.5 để giúp cho việc
xử lý chuỗi trong môi trường đa tiểu trình được hiệu quả hơn
Đây cũng là lớp final nên không thể tạo lớp con từ nó
public final class StringBuilder
extends Object
implements Serializable, CharSequence
Do hai lớp này khá giống nhau nên trong phần tiếp theo sẽ trình bày StringBuffer làm đại diện
Trang 93.2 Khởi tạo
StringBuffer(CharSequence
Bảng 5: Các constructor của StringBuffer
Lưu ý: Không thể tạo StringBuffer và StringBuilder từ string literal mà phải gọi constructor tương ứng
StringBuffer sb=new StringBuffer( "Hello Java" ); // Đúng
3.3 Các phương thức chính
Dưới đây là một số phương thức đặc trưng của StringBuffer Ngoài những phương thức dưới đây, StringBuffer còn có một số phương thức tương tự như String
StringBuffer append(Object
obj)
Ghép dạng chuỗi của obj (sau khi gọi toString()) vào cuối this
Ngoài ra còn có các dạng overload với kiểu cơ bản (int, char, float, ), kiểu CharSequence,
String và StringBuffer
Xóa dãy ký tự từ vị trí start tới vị trí end-1 Nếu
start bằng end thì sẽ không có chuyện gì xảy ra
StringBuffer
offset, Object obj)
Chèn một đối tượng vào vị trí offset Ngoài ra còn có các dạng overload với kiểu cơ bản (int, char, float, ), kiểu CharSequence và
String
start, int end, String str) Thay thế dãy ký tự từ vị trí start tới vị trí end-1
bởi chuỗi str Trước tiên phần ký tự trong khoảng
đó sẽ được xóa, sau đó chuỗi str sẽ được chèn vào
vị trí start Nếu start bằng end thì
Trang 10StringBuffer sẽ không bị xóa còn str vẫn được chèn vào
Trả về chuỗi con là dãy ký tự từ vị trí start tới vị trí end
Bảng 6: Một số phương thức của StringBuffer
Ví dụ:
StringBuffer s=new StringBuffer( "Java" );
s.append( " StringBuffer" );
StringBuffer s=new StringBuffer( "Java StringBuffer" );
s.reverse();
StringBuffer s=new StringBuffer( "Java Strings" );
s.insert(5, "ABCD " );
StringBuffer s=new StringBuffer( "Java StringBuffer" );
StringBuffer s=new StringBuffer( "Java StringBuffer" );
4 StringBuffer hay String
4.1 Cách lựa chọn
Ta thấy: String thì gọn nhẹ nhưng thiếu chức năng, phải sinh ra đối tượng mới nếu cần
"thay đổi" nội dung Còn StringBuffer thì cồng kềnh nhưng nhiều chức năng và không cần sinh ra đối tượng mới khi xử lý thay đổi nội dung chuỗi Do đó mỗi lớp có ưu-khuyết điểm riêng và cần cân nhắc khi sử dụng chúng
Nếu trong chương trình chỉ cần lưu trữ chuỗi mà ít khi có thao tác thay đổi giá trị của chuỗi: dùng String sẽ sẽ tăng tốc độ xử lý và tiết kiệm bộ nhớ do String
gọn nhẹ hơn StringBuffer
Trang 11 Nếu trong chương trình có nhiều thao tác thay đổi chuỗi: dùng StringBuffer sẽ tăng tốc độ xử lý và tiết kiệm bộ nhớ do không cần sinh ra các đối tượng
StringBuffer trung gian để làm việc "đàng sau sân khấu"
4.2 Ví dụ
Xem ví dụ sau: (trong ví dụ là hai đoạn code đạt kết quả sau cùng giống nhau: cộng các
số từ 0 tới 9.999 vào chuỗi s Cách 1 chỉ dùng String, Cách 2 dùng StringBuffer làm trung gian.)
// Đoạn code 1: dùng String
{
String s= "" ;
for(int i=0;i<n;i++)
s+=i;
time=System.currentTimeMillis()-time;
System.out.println( "Thoi gian xu ly: " +time+ "ms" );
}
// Đoạn code 2: dùng StringBuffer
{
String s= "" ;
StringBuffer sb=new StringBuffer();
sb.append(i);
s=sb.toString();
time=System.currentTimeMillis()-time;
System.out.println( "Thoi gian xu ly: " +time+ "ms" );
}
Kết quả tham khảo (kết quả thực tế tùy vào cấu hình máy):
Thoi gian xu ly: 4875ms
Thoi gian xu ly: 16ms
Press any key to continue
Ta thấy kết quả rất chênh lệnh, dù có đảo khối lệnh của Cách 2 lên trước Cách 1 cũng không đổi (loại trừ khả năng Java "cache" kết quả của lần xử lý trước) Dĩ nhiên là nếu dùng StringBuilder thì kết quả của Cách 2 sẽ càng khả quan hơn (về mặt lý thuyết) do
ta chỉ chạy đơn tiểu trình