[img]data/attachments/7/7891-4418fd2719a2edbe252c0b66545c66ef.jpg[/img]

giới thiệu về các sưu tập
hầu hết các ứng dụng phần mềm của thế giới thực đều có liên quan đến các sưu tập sự vật nào đó (các tệp, các biến, các dòng của tệp, …). thông thường, các chương trình hướng đối tượng đều có liên quan đến sưu tập các đối tượng. ngôn ngữ java có một khung công tác các sưu tập (collections framework) khá tinh vi cho phép bạn tạo và quản lý các sưu tập đối tượng thuộc các kiểu khác nhau. bản thân khung công tác này đã có thể đủ để viết riêng nguyên cả một cuốn sách hướng dẫn, do đó chúng tôi sẽ không bàn tất cả trong tài liệu này. thay vào đó, chúng tôi sẽ đề cập đến sưu tập thường dùng nhất và một vài kỹ thuật sử dụng nó. những kỹ thuật đó áp dụng cho hầu hết các sưu tập có trong ngôn ngữ java.

mảng
hầu hết các ngôn ngữ lập trình đều có khái niệm mảng để chứa một sưu tập các sự vật và java cũng không ngoại lệ. mảng thực chất là một sưu tập các phần tử có cùng kiểu.
có hai cách để khai báo một mảng:
* tạo một mảng có kích thước cố định và kích thước này không bao giờ thay đổi.
* tạo một mảng với một tập các giá trị ban đầu. kích thước của tập giá trị này sẽ quyết định kích cỡ của mảng – nó sẽ vừa đủ lớn để chứa toàn bộ các giá trị đó. sau đó thì kích cỡ này sẽ cố định mãi.
nói chung, bạn khai báo một mảng như sau:


Mã nguồn PHP:
new  
Mã nguồn PHP:
[color=#0000bb]elementtype [/color][color=#007700][ [/color][color=#0000bb]arraysize [/color][color=#007700]][/color] 
để tạo một mảng số nguyên gồm có 5 phần tử, bạn phải thực hiện theo một trong hai cách sau:


Mã nguồn PHP:
[color=#000000]
int[/color][color=#007700][] [/color][color=#0000bb]integers [/color][color=#007700]= new [/color][color=#0000bb]int[/color][color=#007700][[/color][color=#0000bb]5[/color][color=#007700]];[/color][color=#0000bb]int[/color][color=#007700][] [/color][color=#0000bb]integers [/color][color=#007700]= new [/color][color=#0000bb]int[/color][color=#007700][] { [/color][color=#0000bb]1[/color][color=#007700], [/color][color=#0000bb]2[/color][color=#007700], [/color][color=#0000bb]3[/color][color=#007700], [/color][color=#0000bb]4[/color][color=#007700], [/color][color=#0000bb]5 [/color][color=#007700]};[/color] 
câu lệnh đầu tiên tạo một mảng rỗng gồm có 5 phần tử. câu lệnh thứ hai là cách tắt để khởi tạo một mảng. câu lệnh này cho phép bạn xác định một danh sách các giá trị khởi tạo, phân tách nhau bằng dấu phẩy (,), nằm trong cặp ngoặc nhọn. chú ý là chúng ta không khai báo kích cỡ trong cặp ngoặc vuông – số các mục trong khối khởi tạo quyết định kích cỡ của mảng là 5 phần tử. cách làm này dễ hơn là tạo một mảng rồi sau đó viết mã lệnh cho một vòng lặp để đặt các giá trị vào, giống như sau:


Mã nguồn PHP:
[color=#000000]
int[/color][color=#007700][] [/color][color=#0000bb]integers [/color][color=#007700]= new [/color][color=#0000bb]int[/color][color=#007700][[/color][color=#0000bb]5[/color][color=#007700]];for ([/color][color=#0000bb]int i [/color][color=#007700]= [/color][color=#0000bb]1[/color][color=#007700]; [/color][color=#0000bb]i [/color][color=#007700]<= [/color][color=#0000bb]integers[/color][color=#007700].[/color][color=#0000bb]length[/color][color=#007700]; [/color][color=#0000bb]i[/color][color=#007700]++) {[/color][color=#0000bb]integers[/color][color=#007700][[/color][color=#0000bb]i[/color][color=#007700]] = [/color][color=#0000bb]i[/color][color=#007700];[/color][color=#0000bb]system[/color][color=#007700].[/color][color=#0000bb]out[/color][color=#007700].print([/color][color=#0000bb]integers[/color][color=#007700][[/color][color=#0000bb]i[/color][color=#007700]] + [/color][color=#dd0000]" "[/color][color=#007700]);}[/color] 
đoạn mã lệnh này cũng khai báo một mảng số nguyên có 5 phần tử. nếu ta thử xếp nhiều hơn 5 phần tử vào mảng này, ta sẽ gặp ngay vấn đề khi chạy đoạn mã lệnh này. để nạp mảng, chúng ta phải lặp đi qua các số nguyên từ 1 cho đến số bằng chiều dài của mảng, chiều dài của mảng ta có thể biết được nhờ truy cập phương thức length() của đối tượng mảng. mỗi lần lặp qua mảng, chúng ta đặt một số nguyên vào mảng. khi gặp số 5 thì dừng lại.
khi mảng đã nạp xong, chúng ta có thể truy nhập vào các phần tử trong mảng nhờ vòng lặp tương tự:


Mã nguồn PHP:
[color=#000000]
[/color][color=#007700]for ([/color][color=#0000bb]int i [/color][color=#007700]= [/color][color=#0000bb]0[/color][color=#007700]; [/color][color=#0000bb]i [/color][color=#007700]< [/color][color=#0000bb]integers[/color][color=#007700].[/color][color=#0000bb]length[/color][color=#007700]; [/color][color=#0000bb]i[/color][color=#007700]++) {[/color][color=#0000bb]system[/color][color=#007700].[/color][color=#0000bb]out[/color][color=#007700].print([/color][color=#0000bb]integers[/color][color=#007700][[/color][color=#0000bb]i[/color][color=#007700]] + [/color][color=#dd0000]" "[/color][color=#007700]);}[/color] 
bạn hãy coi mảng như một dãy các thùng. mỗi phần tử trong mảng nằm trong một thùng, mỗi thùng được gán một chỉ số khi bạn tạo mảng. bạn truy nhập vào các phần tử nằm trong thùng cụ thể nào đó bằng cách viết:


Mã nguồn PHP:
arrayname [  
Mã nguồn PHP:
[color=#0000bb]elementindex [/color][color=#007700]][/color] 
chỉ số của mảng bắt đầu từ 0, có nghĩa là phần tử đầu tiên ở vị trí số 0. điều đó làm cho vòng lặp thêm ý nghĩa. chúng ta bắt đầu vòng lặp bằng số 0 vì mảng được đánh chỉ số bắt đầu từ 0 và chúng ta lặp qua từng phần tử trong mảng, in ra giá trị của từng chỉ số phần tử.

sưu tập là gì?
mảng cũng tốt, nhưng làm việc với chúng cũng có đôi chút bất tiện. nạp giá trị cho mảng cũng mất công, và một khi khai báo mảng, bạn chỉ có thể nạp vào mảng những phần tử đúng kiểu đã khai báo và với số lượng phần tử đúng bằng số lượng mà mảng có thể chứa. mảng chắc chắn là không có vẻ hướng đối tượng lắm. thực tế, lý do chính để java có mảng là vì nó được giữ lại để dùng như di sản từ những ngày tiền lập trình hướng đối tượng. mảng có trong mọi phần mềm, bởi vậy không có mảng sẽ khiến cho ngôn ngữ khó mà tồn tại trong thế giới thực, đặc biệt khi bạn phải tương tác với các hệ thống khác có dùng mảng. nhưng java cung cấp cho bạn nhiều công cụ để quản lý sưu tập hơn. những công cụ này thực sự rất hướng đối tượng.

khái niệm sưu tập không khó để có thể hiểu được. khi bạn cần một số lượng cố định các phần tử có cùng kiểu, bạn có thể dùng mảng. khi bạn cần các phần tử có kiểu khác nhau hoặc số lượng các phần tử có thể thay đổi linh hoạt, bạn dùng sưu tập của java.

danh sách mảng
trong tài liệu này, chúng tôi sẽ đề cập đến chỉ một dạng của sưu tập, đó là arraylist. trong lúc trình bày, bạn sẽ biết được một lý do khác khiến cho nhiều người thuần túy chủ nghĩa hướng đối tượng công kích ngôn ngữ java.

để dùng arraylist, bạn phải thêm một lệnh quan trọng vào lớp của mình:


Mã nguồn PHP:
[color=#000000]
import java[/color][color=#007700].[/color][color=#0000bb]util[/color][color=#007700].[/color][color=#0000bb]arraylist[/color][color=#007700];[/color] 
bạn khai báo một arraylist rỗng như sau:


Mã nguồn PHP:
arraylist referencevariablename = new  
Mã nguồn PHP:
[color=#0000bb]arraylist[/color][color=#007700]();[/color] 
bổ sung và loại bỏ các phần tử trong danh sách khá dễ dàng. có nhiều phương thức để làm điều ấy, nhưng có hai phương thức thường dùng nhất như sau:


Mã nguồn PHP:
[color=#000000]
somearraylist[/color][color=#007700].[/color][color=#0000bb]add[/color][color=#007700]([/color][color=#0000bb]someobject[/color][color=#007700]);[/color][color=#0000bb]object removedobject [/color][color=#007700]= [/color][color=#0000bb]somearraylist[/color][color=#007700].[/color][color=#0000bb]remove[/color][color=#007700]([/color][color=#0000bb]someobject[/color][color=#007700]);[/color] 
đóng hộp và mở hộp các kiểu nguyên thủy.
các sưu tập java chứa các đối tượng, chứ không phải là các kiểu nguyên thủy. mảng có thể chứa cả hai, nhưng lại không hướng đối tượng như ta muốn. nếu bạn muốn lưu trữ bất cứ kiểu gì là kiểu con của object vào một danh sách, bạn đơn giản chỉ cần gọi một trong số nhiều phương thức của arraylist để làm việc này. cách đơn giản nhất là:


Mã nguồn PHP:
referencevariablename
Mã nguồn PHP:
[color=#0000bb]add[/color][color=#007700]([/color][color=#0000bb]someobject[/color][color=#007700]);[/color] 
câu lệnh này thêm một đối tượng vào cuối danh sách. cho đến đây mọi việc đều ổn. nhưng liệu điều gì sẽ xảy ra khi bạn muốn thêm một kiểu nguyên thủy vào danh sách? bạn không thể làm việc này trực tiếp. thay vào đó, bạn phải bọc kiểu nguyên thủy thành đối tượng. mỗi kiểu nguyên thủy có một lớp bao bọc tương ứng:
* boolean dành cho các boolean
* byte dành cho các byte
* character dành cho các char
* integer dành cho các int
* short dành cho các short
* long dành cho các long
* float dành cho các float
* double dành cho các double
ví dụ, để đưa kiểu nguyên thủy int vào một arraylist, chúng ta sẽ phải viết mã lệnh như sau:


Mã nguồn PHP:
[color=#000000]
integer boxedint [/color][color=#007700]= new [/color][color=#0000bb]integer[/color][color=#007700]([/color][color=#0000bb]1[/color][color=#007700]);[/color][color=#0000bb]somearraylist[/color][color=#007700].[/color][color=#0000bb]add[/color][color=#007700]([/color][color=#0000bb]boxedint[/color][color=#007700]);[/color] 
bao bọc kiểu nguyên thủy trong một cá thể của lớp bao bọc (wrapper) cũng được gọi là thao tác đóng hộp (boxing) kiểu nguyên thủy. để nhận lại kiểu nguyên thủy ban đầu, ta phải mở hộp (unboxing) nó. có nhiều phương thức hữu dụng trong các lớp bao bọc, nhưng sử dụng chúng khá phiền toái đối với hầu hết các lập trình viên vì nó đòi hỏi nhiều thao tác phụ thêm để sử dụng kiểu nguyên thủy với các sưu tập. java 5.0 đã giảm bớt những vất vả ấy bằng cách hỗ trợ các thao tác đóng hộp/mở hộp tự động.

sử dụng các sưu tập
trong đời thực hầu hết người trưởng thành đều mang theo tiền. giả sử các adult đều có ví để đựng tiền của mình. với hướng dẫn này, chúng ta sẽ giả sử rằng:
* chỉ các tờ giấy bạc là biểu hiện của tiền tệ
* mệnh giá của tờ giấy bạc (như một số nguyên) đồng nhất với tờ giấy bạc đó.
* tất cả tiền trong ví đều là đô la mỹ.
* mỗi đối tượng adult khởi đầu cuộc đời được lập trình của nó không có đồng tiền nào
bạn nhớ mảng các số nguyên chứ? thay vào đó ta hãy tạo một arraylist. nhập khẩu gói arraylist, sau đó thêm một arraylist vào lớp adult ở cuối danh sách các biến cá thể khác:


Mã nguồn PHP:
protected  
Mã nguồn PHP:
[color=#0000bb]arraylist wallet [/color][color=#007700]= new [/color][color=#0000bb]arraylist[/color][color=#007700]();[/color] 
chúng ta tạo một arraylist và khởi tạo nó là danh sách rỗng vì đối tượng adult phải kiếm từng đồng đô la. chúng ta cũng có thể bổ sung thêm vài phương thức truy cập wallet nữa:


Mã nguồn PHP:
[color=#000000]
[/color][color=#007700]public [/color][color=#0000bb]arraylist getwallet[/color][color=#007700]() {return [/color][color=#0000bb]wallet[/color][color=#007700];}public [/color][color=#0000bb]void setwallet[/color][color=#007700]([/color][color=#0000bb]arraylist awallet[/color][color=#007700]) {[/color][color=#0000bb]wallet [/color][color=#007700]= [/color][color=#0000bb]awallet[/color][color=#007700];}[/color] 
cung cấp những phương thức truy cập nào là tùy theo óc suy xét, nhưng trong trường hợp này ta đi đến những phương thức truy cập thông thường. chẳng có lý do gì mà chúng ta không thể gọi setwallet() giống như gọi resetwallet(), hay thậm chí là gobankrupt() vì chúng ta đang thiết đặt lại nó thành arraylist rỗng. liệu một đối tượng khác có thể thiết đặt lại wallet của chúng ta với một giá trị mới không? một lần nữa ta lại phải viện đến óc xét đoán. đó là những gì mà thiết kế hướng đối tượng tính đến (ood)!
bây giờ chúng ta sẽ thiết đặt mọi thứ để bổ sung một vài phương thức cho phép ta tương tác với wallet:


Mã nguồn PHP:
[color=#000000]
[/color][color=#007700]public [/color][color=#0000bb]void addmoney[/color][color=#007700]([/color][color=#0000bb]int bill[/color][color=#007700]) {[/color][color=#0000bb]integer boxedbill [/color][color=#007700]= new [/color][color=#0000bb]integer[/color][color=#007700]([/color][color=#0000bb]bill[/color][color=#007700]);[/color][color=#0000bb]wallet[/color][color=#007700].[/color][color=#0000bb]add[/color][color=#007700]([/color][color=#0000bb]boxedbill[/color][color=#007700]);}public [/color][color=#0000bb]void spendmoney[/color][color=#007700]([/color][color=#0000bb]int bill[/color][color=#007700]) {[/color][color=#0000bb]integer boxedbill [/color][color=#007700]= new [/color][color=#0000bb]integer[/color][color=#007700]([/color][color=#0000bb]bill[/color][color=#007700]);[/color][color=#0000bb]boolean havethatbill [/color][color=#007700]= [/color][color=#0000bb]wallet[/color][color=#007700].[/color][color=#0000bb]contains[/color][color=#007700]([/color][color=#0000bb]boxedbill[/color][color=#007700]); if([/color][color=#0000bb]havethatbill[/color][color=#007700]) {[/color][color=#0000bb]wallet[/color][color=#007700].[/color][color=#0000bb]remove[/color][color=#007700]([/color][color=#0000bb]boxedbill[/color][color=#007700]);} else {[/color][color=#0000bb]system[/color][color=#007700].[/color][color=#0000bb]out[/color][color=#007700].[/color][color=#0000bb]println[/color][color=#007700]([/color][color=#dd0000]"i don't have that bill."[/color][color=#007700]);}}[/color] 
chúng ta sẽ nghiên cứu chi tiết hơn trong mấy phần tiếp theo đây.

tương tác với sưu tập
phương thức addmoney() cho phép chúng ta đưa thêm một tờ giấy bạc vào ví. ta hãy nhớ lại rằng tờ giấy bạc của chúng ta ở đây chỉ đơn giản là những số nguyên. để thêm chúng vào sưu tập, ta phải bao bọc một số kiểu int thành đối tượng integer.
phương thức spendmoney() lại nhảy vũ điệu đóng hộp để kiểm tra tờ giấy bạc có trong wallet không bằng cách gọi contains(). nếu ta có tờ giấy bạc đó, ta gọi remove() để lấy nó đi. nếu ta không thực hiện thì ta cũng nói như vậy.
hãy dùng các phương thức này trong main(). thay thế nội dung hiện tại trong main() bằng nội dung sau:


Mã nguồn PHP:
[color=#000000]
[/color][color=#007700]public static [/color][color=#0000bb]void main[/color][color=#007700]([/color][color=#0000bb]string[/color][color=#007700][] [/color][color=#0000bb]args[/color][color=#007700]) {[/color][color=#0000bb]adult myadult [/color][color=#007700]= new [/color][color=#0000bb]adult[/color][color=#007700](); [/color][color=#0000bb]myadult[/color][color=#007700].[/color][color=#0000bb]addmoney[/color][color=#007700]([/color][color=#0000bb]5[/color][color=#007700]);[/color][color=#0000bb]myadult[/color][color=#007700].[/color][color=#0000bb]addmoney[/color][color=#007700]([/color][color=#0000bb]1[/color][color=#007700]);[/color][color=#0000bb]myadult[/color][color=#007700].[/color][color=#0000bb]addmoney[/color][color=#007700]([/color][color=#0000bb]10[/color][color=#007700]); [/color][color=#0000bb]stringbuffer bills [/color][color=#007700]= new [/color][color=#0000bb]stringbuffer[/color][color=#007700]();[/color][color=#0000bb]iterator iterator [/color][color=#007700]= [/color][color=#0000bb]myadult[/color][color=#007700].[/color][color=#0000bb]getwallet[/color][color=#007700]().[/color][color=#0000bb]iterator[/color][color=#007700]();while ([/color][color=#0000bb]iterator[/color][color=#007700].[/color][color=#0000bb]hasnext[/color][color=#007700]()) {[/color][color=#0000bb]integer boxedinteger [/color][color=#007700]= (integer) [/color][color=#0000bb]iterator[/color][color=#007700].[/color][color=#0000bb]next[/color][color=#007700]();[/color][color=#0000bb]bills[/color][color=#007700].[/color][color=#0000bb]append[/color][color=#007700]([/color][color=#0000bb]boxedinteger[/color][color=#007700]);}[/color][color=#0000bb]system[/color][color=#007700].[/color][color=#0000bb]out[/color][color=#007700].[/color][color=#0000bb]println[/color][color=#007700]([/color][color=#0000bb]bills[/color][color=#007700].[/color][color=#0000bb]tostring[/color][color=#007700]());}[/color] 
cho đến thời điểm này ta thấy phương thức main() tổng hợp rất nhiều thứ. đầu tiên, chúng ta gọi phương thức addmoney() vài lần để nhét tiền vào trong wallet. sau đó ta lặp đi qua nội dung của wallet để in ra những gì có trong đó. chúng ta dùng vòng lặp while để làm điều này, nhưng ta còn phải làm thêm một số việc nữa. đó là:
* lấy một iterator cho danh sách, nó sẽ giúp chúng ta truy nhập từng phần tử trong danh sách.
* gọi hasnext() của iterator với vai trò biểu thức logic để chạy vòng lặp xem liệu ta có còn phần tử nào cần xử lý nữa không
* gọi next() của iterator để lấy phần tử tiếp theo mỗi lần đi qua vòng lặp
* ép kiểu đối tượng trả về thành kiểu mà ta biết trong danh sách (trong trường hợp này là integer)
đó là cách diễn đạt chuẩn dành cho vòng lặp qua một sưu tập trong ngôn ngữ java. một cách làm khác là ta có thể gọi phương thức toarray() của danh sách và nhận lại một mảng, sau đó ta có thể lặp qua mảng này, sử dụng vòng for như ta đã làm với vòng while. cách làm hướng đối tượng hơn là khai thác sức mạnh của khung công tác sưu tập của java.
khái niệm mới duy nhất ở đây là ý tưởng ép kiểu (casting). đó là gì? như ta đã biết, đối tượng trong ngôn ngữ java có kiểu, hay là lớp. nếu bạn nhìn vào chữ ký của phương thức next(), bạn sẽ thấy nó trả lại một object, chứ không phải là một lớp con cụ thể của object. tất cả các đối tượng trong thế giới lập trình java đều là lớp con của object, nhưng java cần biết kiểu chính xác của đối tượng để bạn có thể gọi các phương thức tương ứng với kiểu mà bạn muốn có. nếu bạn không ép kiểu, bạn sẽ bị giới hạn chỉ được dùng các phương thức có sẵn dành cho object, thực sự chỉ gồm một danh sách ngắn mà thôi. trong ví dụ cụ thể này, chúng ta không cần gọi bất kỳ phương thức nào của integer mà không có trong danh sách, nhưng nếu ta cần gọi thì ta phải ép kiểu trước đã.