ìm hi u Java Collections Framework
Tr l i Liên h
Khung c ng tác c a các s u t p Java (Java Collections Framework) là r ng l n. Có r t nhi u l p và giao di n trong khung ư
c ng tác. T i đây, chúng ta s trình bày nhi u h n, dù không ph i là t t c v chúng. ơ
Các giao di n và các l p s u t p ư
Khung công tác c a các s u t p Java d a trên tri n khai th c hi n c th m t s giao di n đ nh nghĩa các ki u s u t p ư ư
(collection):
Giao di n List đ nh nghĩa m t s u t p các ph n t Object có th d n h ng. ư ướ
Giao di n Set đ nh nghĩa m t s u t p không có các ph n t trùng l p. ư
Giao di n Map đ nh nghĩa m t s u t p các c p khóa - giá tr . ư
Chúng ta s nói v m t vài tri n khai th c hi n c th trong h ng d n này. Đây không ph i là m t danh sách đ y đ , ướ
nh ng nhi u kh năng b n th ng xuyên th y nh ng th sau đây trong các d án phát tri n b ng ngôn ng Java:ư ườ
Giao di n Các tri n khai th c hi n
List ArrayList, Vector
Set HashSet, TreeSet
Map HashMap
T t c các giao di n trong khung c ng tác, tr Map là các giao di n con c a giao di n Collection, trong đó đ nh nghĩa c u
trúc chung nh t c a m t s u t p. M i s u t p g m nhi u ph n t . V i vai trò là trình th c hi n các giao di n con c a ư ư
Collection, t t c ki u s u t p chia s chung (theo tr c giác) m t s hành vi: ư
Các ph ng th c đ mô t kích th c c a s u t p (nh size() và isEmpty()).ươ ướ ư ư
Các ph ng th c đ mô t n i dung c a s u t p (nh contains() và containsAll()).ươ ư ư
Các ph ng th c đ h tr thao tác v n i dung c a s u t p (nh add(), remove() và clear()).ươ ư ư
Các ph ng th c đ cho phép b n chuy n đ i m t s u t p thành m t m ng (nh toArray()).ươ ư ư
M t ph ng th c đ cho phép b n nh n đ c m t trình vòng l p (iterator) trên m ng các ph n t (iterator()). ươ ượ
Chúng ta s nói v m t s ph ng th c trên trong ph n này. Đ ng th i chúng ta s th o lu n trình vòng l p (iterator) là gì ươ
và cách s d ng nó nh th nào. ư ế
L u ý r ng các Map là đ c bi t. Th t s chúng hoàn toàn không là m t s u t p. Tuy nhiên, chúng có hành vi r t gi ng cácư ư
s u t p, vì v y chúng ta cũng nói v chúng trong ph n này.ư
Các tri n khai th c hi n Danh sách (List)
Các phiên b n cũ h n c a JDK ch a m t l p đ c g i là ơ ượ Vector. Nó v n còn có trong các phiên b n m i h n, nh ng b n ơ ư
ch nên s d ng nó khi b n c n có m t s u t p đ ng b hoá -- đó là, m t trong nh ng y u t là an toàn phân lu ng. (Nói ư ế
v phân lu ng đã v t ra ngoài ph m vi c a bài vi t này, chúng ta s th o lu n ng n g n v khái ni m y trong ph n ượ ế
Tóm t t). Trong các tr ng h p khác, b n nên s d ng l p ườ ArrayList. B n v n có th s d ng Vector, nh ng nó áp đ t ư
m t s chi phí thêm mà b n th ng không c n. ườ
M t ArrayList là cái nh tên c a nó g i ý: danh sách các ph n t theo th t . Chúng ta đã th y làm th nào đ t o ra m tư ế
danh sách và làm th nào đ thêm các ph n t vào nó, trong bài h ng d n gi i thi u tr c. Khi chúng ta t o ra m t l pế ướ ướ
Wallet l ng trong trong h ng d n này, chúng ta đã tích h p vào đó m t ướ ArrayList đ gi các hoá đ n thanh toán c a ơ
Adult:
Ph ng th c getMoneyTotal() s d ng m t trình vòng l p (iterator) đ duy t qua danh sách các hoá đ n thanh toán và tínhươ ơ
t ng giá tr c a chúng. M t Iterator t ng t nh m t Enumeration trong các phiên b n cũ h n c a ngôn ng Java. Khi b n ươ ư ơ
nh n đ c m t trình vòng l p trên s u t p (b ng cách g i iterator()), trình vòng l p cho phép b n duy t qua (traverse) toàn ượ ư
b s u t p b ng cách s d ng m t s ph ng th c quan tr ng, đ c minh h a trong mã l nh trên: ư ươ ượ
hasNext() cho b n bi t còn có m t ph n t ti p theo khác trong s u t p không. ế ế ư
next() cho b n ph n t ti p theo đó. ế
Nh chúng ta đã th o lu n trên, b n ph i ép ki u đúng khi b n trích ra các ph n t t s u t p khi s d ng next().ư ư
Tuy nhiên, Iterator còn cho chúng ta m t s kh năng b sung thêm. Chúng ta có th lo i b các ph n t kh i l p ArrayList
b ng cách g i remove() (hay removeAll(), hay clear()), nh ng chúng ta cũng có th s d ng Iterator đ làm đi u đó. Hãy ư
thêm m t ph ng th c r t đ n gi n đ c g i là spendMoney() t i Adult: ươ ơ ượ
Ph ng th c này g i removeBill() trên Wallet:ươ
Chúng ta nh n đ c m t Iterator trên các hoá đ n thanh toán ArrayList, và duy t qua danh sách đ tìm m t k t qu kh p ượ ơ ế
v i giá tr hóa đ n đ c chuy n qua (aBill). N u chúng ta tìm th y m t k t qu kh p, chúng ta g i remove() trên trình ơ ượ ế ế
vòng l p đ lo i b hóa đ n đó. Cũng đ n gi n, nh ng còn ch a ph i là đ n gi n h t m c. Mã d i đây th c hi n cùng ơ ơ ư ư ơ ế ướ
m t công vi c và d đ c h n nhi u: ơ
Có th b n s không th ng xuyên g i remove() trên m t Iterator nh ng s r t t t n u có công c đó khi b n c n nó. ườ ư ế
Lúc này, chúng ta có th lo i b ch m t hóa đ n riêng l m i l n kh i Wallet. S là t t h n n u s d ng s c m nh c a ơ ơ ế
m t List đ giúp chúng ta lo i b nhi u hóa đ n cùng m t lúc, nh sau: ơ ư
Chúng ta c n ph i thêm removeBills() vào wallet c a chúng ta đ th c hi n vi c này. Hãy th mã d i đây: ướ
Đây là vi c tri n khai th c hi n d dàng nh t mà chúng ta có th s d ng. Chúng ta g i removeAll() trên List các hoá đ n ơ
c a chúng ta, chuy n qua m t Collection. Sau đó ph ng th c này lo i b t t c các ph n t kh i danh sách có trong ươ
Collection. Hãy th ch y mã d i đây: ướ
Các k t qu không ph i là nh ng gì mà chúng ta mu n. Chúng ta đã k t thúc mà không còn hóa đ n nào trong ví c . T iế ế ơ
sao? B i vì removeAll() lo i b t t c các k t qu kh p. Nói cách khác, b t kỳ và t t c các k t qu kh p v i m t m c ế ế
trong List mà chúng ta chuy n cho ph ng th c đ u b lo i b . Các hoá đ n thanh toán mà chúng ta đã chuy n cho ph ng ươ ơ ươ
th c có ch a 1 và 2. Ví c a chúng ta có ch a hai s 1 và m t s 2. Khi removeAll() tìm ki m k t qu kh p v i ph n t s ế ế
1, nó tìm th y hai k t qu kh p và lo i b chúng c hai. Đó không ph i là nh ng gì mà chúng ta mu n! Chúng ta c n thay ế
đ i mã c a chúng ta trong removeBills() đ s a l i đi u này:
Mã này ch lo i b m t k t qu kh p riêng r , ch không ph i là t t c các k t qu kh p. Nh c n th n v i removeAll(). ế ế
Tri n khai th c hi n t p h p
Có hai tri n khai th c hi n T p h p (Set) th ng đ c s d ng ph bi n: ườ ượ ế
HashSet, không đ m b o th t vòng l p.
TreeSet, b o đ m th t vòng l p.
Các tài li u h ng d n ngôn ng Java g i ý r ng b n s đi đ n ch s d ng tri n khai th c hi n th nh t trong h u h t ướ ế ế
các tr ng h p. Nói chung, n u b n c n ph i ch c ch n r ng các ph n t trong Set c a b n x p theo m t th t nh tườ ế ế
đ nh nào đó khi b n duy t qua nó b ng m t trình vòng l p, thì hãy s d ng tri n khai th c hi n th hai. N u không, s ế
d ng cách th nh t. Th t c a các ph n t trong m t TreeSet (có th c hi n giao di n SortedSet) đ c g i là th t t ượ
nhiên (natural ordering); đi u này có nghĩa là, h u h t m i tr ng h p, b n s có kh năng s p x p các ph n t d a trên ế ườ ế
phép so sánh equals().
Gi s r ng m i Adult có m t t p h p các bi t hi u. Chúng ta th c s không quan tâm đ n chúng đ c s p đ t th nào, ế ượ ế
nh ng các b n sao s không có ý nghĩa. Chúng ta có th s d ng m t HashSet đ l u gi chúng. Tr c tiên, chúng ta thêmư ư ướ
m t bi n cá th : ế
Sau đó chúng ta thêm m t ph ng th c đ thêm bi t hi u vào Set: ươ
Bây gi hãy th ch y mã này:
B n s th y ch có m t Bobby đ n l xu t hi n trên màn hình. ơ
Các tri n khai th c hi n Map
Map (Ánh x ) là m t t p h p các c p khóa - giá tr . Nó không th ch a các khóa gi ng h t nhau. M i khóa ph i ánh x t i
m t giá tr đ n l , nh ng giá tr đó có th là b t kỳ ki u gì. B n có th nghĩ v m t ánh x nh là List có đ t tên. Hãy ơ ư ư
t ng t ng m t List trong đó m i ph n t có m t tên mà b n có th s d ng đ trích ra ph n t đó tr c ti p. Khóa cóưở ượ ế
th là b t c cái gì ki u Object, gi ng nh giá tr . M t l n n a, đi u đó có nghĩa là b n không th l u tr các giá tr ki u ư ư
nguyên th y (primitive) tr c ti p vào trong m t Map (b n có ghét các giá tr ki u nguyên th y không đ y ?). Thay vào đó, ế
b n ph i s d ng các l p bao gói ki u nguyên th y đ l u gi các giá tr đó. ư
M c dù đây là m t chi n l c tài chính m o hi m, chúng ta s cung c p cho m i Adult m t t p h p các th tín d ng đ n ế ượ ơ
gi n nh t có th ch p nh n đ c. M i th s có m t tên và m t s d (ban đ u là 0). Tr c tiên, chúng ta thêm m t bi n ượ ư ướ ế
cá th :
Sau đó chung ta thêm m t ph ng th c đ b sung thêm m t th tín d ng (CreditCard)t i Map: ươ
Giao di n c a Map khác v i các giao di n c a các s u t p khác. B n g i put() v i m t khóa và m t giá tr đ thêm m t ư
m c vào ánh x . B n g i get() v i khóa đ trích ra m t giá tr . Chúng ta s làm vi c này trong m t ph ng th c đ hi n ươ
th s d c a m t th : ư
T t c nh ng gì còn l i là thêm ph ng th c charge() đ cho phép c ng thêm vào s d c a chúng ta: ươ ư
Bây gi hãy th ch y mã d i đây, nó s hi n th cho b n 19.95 trên màn hình. ướ
M t th tín d ng đi n hình có m t tên, m t s tài kho n, m t h n m c tín d ng và m t s d . M i m c trong m t Map ư
ch th có m t khóa và m t giá tr . Các th tín d ng r t đ n gi n c a chúng ta r t phù h p, b i vì chúng ch có m t tên ơ
và m t s d hi n t i. Chúng ta có th làm cho ph c t p h n b ng cách t o ra m t l p đ c g i là CreditCard, v i các ư ơ ượ
bi n cá th dành cho t t c các đ c tính c a m t th tín d ng, sau đó l u tr các cá th c a l p này nh các giá tr cho cácế ư ư
m c trong Map c a chúng ta.
Có m t s khía c nh thú v khác v giao di n Map đ trình bày tr c khi chúng ta đi ti p (đây không ph i là m t danh sách ướ ế
đ y đ ):
Ph ng th cươ Hành vi
containsKey() Tr l i Map có ch a khóa đã cho hay không.
containsValue() Tr l i Map có ch a giá tr đã cho hay không.
keySet() Tr v m t Set t p h p các khóa.
values() Tr v m t Set t p h p các giá tr .
entrySet() Tr v m t Set t p h p các c p khóa - giá tr , đ c đ nh nghĩa nh là các cá th c a các Map.Entry. ượ ư
remove() Cho phép b n lo i b giá tr cho m t khóa đã cho.
isEmpty() Tr l i Map có r ng không (r ng có nghĩa là, không ch a khóa nào).
M t s trong các ph ng th c này, ch ng h n nh isEmpty() ch là đ cho ti n thôi, nh ng m t s là r t quan tr ng. Ví ươ ư ư
d , cách duy nh t đ th c hi n vòng l p qua các ph n t trong m t Map là thông qua m t trong các t p h p có liên quan
(t p h p các khóa, các giá tr , ho c các c p khóa-giá tr ).
L p Collections
Khi b n đang s d ng khung c ng tác các s u t p Java, b n c n ph i n m đ c nh ng gì có s n trong l p Collections. ư ượ
L p này g m có m t kho l u tr các ph ng th c tĩnh đ h tr các thao tác trên s u t p. Chúng tôi s không trình bày t t ư ươ ư
c chúng đây, b i vì b n có th t mình đ c API, nh ng chúng tôi s trình bày hai ph ng th c th ng xuyên xu t hi n ư ươ ườ
trong mã Java:
copy()
sort()
Ph ng th c đ u tiên cho phép b n sao chép các n i dung c a m t s u t p này t i m t s u t p khác, nh sau:ươ ư ư ư
Mã này sao chép t ngu n (source) vào đích (target). Đích ph i có cùng kích th c nh ngu n, vì th b n không th sao ướ ư ế
chép m t List vào m t List r ng.
Ph ng th c sort() s p x p các ph n t theo th t t nhiên c a chúng. T t c các ph n t ph i tri n khai th c hi n giaoươ ế
di n Comparable sao cho chúng có th so sánh v i nhau. Các l p có s n gi ng nh String đã th c hi n đi u này. Vì v y, ư
đ i v i m t t p h p các chu i ký t , chúng ta có th s p x p chúng theo th t tăng d n theo ki u biên so n t đi n b ng ế
mã sau đây:
B n s nh n đ c [four, one, three, two] trên màn hình. Nh ng b n có th s p x p các l p mà b n t o ra nh th nào? ượ ư ế ư ế
Chúng ta có th làm đi u này cho Adult. Tr c tiên, chúng ta làm cho l p Adult có th so sánh l n nhau: ướ
Sau đó, chúng ta ghi đè compareTo() đ so sánh hai cá th Adult Chúng ta s duy trì vi c so sánh r t đ n gi n đ làm ví d , ơ
do đó nó làm r t ít vi c:
B t kỳ s nào nh h n 0 có nghĩa là "bé h n", và -1 là giá tr thích h p đ s d ng. T ng t , 1 là thu n ti n đ dành cho ơ ơ ươ
"l n h n". Nh b n có th th y, 0 có nghĩa là "b ng nhau". So sánh hai đ i t ng theo cách này rõ ràng là m t quá trình ơ ư ượ
th công. B n c n ph i đi qua các bi n cá th và so sánh t ng bi n. Trong tr ng h p này, chúng ta so sánh tên và h ế ế ườ
s p x p th c t theo h . Nh ng b n nên bi t, t i sao ví d c a chúng ta l i r t đ n gi n. M i Adult có nhi u h n là ch ế ế ư ế ơ ơ
tên và h . N u chúng ta mu n làm m t phép so sánh sâu h n, chúng ta s ph i so sánh các Wallet c a m i Adult đ xem ế ơ
xem chúng có b ng nhau không, nghĩa là chúng ta s ph i tri n khai th c hi n compareTo() trên Wallet và ph n còn l i.
Ngoài ra, đ th t chính xác khi so sánh, b t c khi nào b n ghi đè compareTo(), b n c n ph i ch c ch n là phép so sánh là
t ng thích v i equals(). Chúng ta không tri n khai th c hi n equals(), vì th chúng ta không lo l ng v vi c t ng thíchươ ế ươ
v i nó, nh ng chúng ta có th ph i làm. Trong th c t , tôi đã th y mã có bao g m m t dòng nh sau, tr c khi tr v ư ế ư ướ
EQUAL:
Cách ti p c n khác đ so sánh các đ i t ng là trích thu t toán trong compareTo() vào m t đ i t ng có ki u Trình so sánhế ượ ượ
(Comparator), sau đó g i Collections.sort() v i s u t p c n s p x p và Comparator, nh sau: ư ế ư
B n s th y "Al Jones" và "Bob Smith", theo th t đó, trong c a s màn hình c a b n.
Có m t s lý do thích đáng đ s d ng cách ti p c n th hai. Các lý do k thu t v t ra ngoài ph m vi c a h ng d n ế ượ ướ
này. Tuy nhiên, t vi n c nh c a phát tri n h ng đ i t ng, đây có th là m t ý t ng t t khi tách bi t ph n mã so sánh ướ ượ ưở
vào trong đ i t ng khác, h n là cung c p cho m i Adult kh năng t so sánh v i nhau. Tuy nhiên, vì đây th c s là nh ng ượ ơ
gì mà equals() th c hi n, m c dù k t qu toán t boolean, có các l p lu n thích h p ng h cho c hai cách ti p c n. ế ế
Using collections
Khi nào b n nên s d ng m t ki u s u t p c th ? Đó là m t phán xét c n đ n năng l c c a b n, và chính vì th mà b n ư ế ế
hy v ng s đ c tr l ng h u hĩ khi là m t l p trình viên. ượ ươ
B t ch p nh ng gì mà nhi u chuyên gia tin t ng, có r t ít các quy t c ch c ch n và nhanh chóng đ xác đ nh c n s ưở
d ng nh ng l p nào trong m t tình hu ng đã cho nào đó. Theo kinh nghi m cá nhân c a tôi, trong ph n l n các l n khi s
d ng các s u t p, m t ArrayList ho c m t HashMap (hãy nh , m t Map không th t s là m t s u t p) đ u b ch i khăm. ư ư ơ
R t có kh năng, b n cũng t ng có tr i nghi m nh v y. D i đây là m t s quy t c ngón tay cái, m t s là hi n nhiên ư ư
h n nh ng cái còn l i:ơ
Khi b n nghĩ r ng mình c n có m t s u t p, hãy b t đ u v i m t List, sau đó c đ cho các mã s báo cho b n ư
bi t có c n m t ki u khác không.ế
N u b n ch c n nhóm các th gì đó, hãy s d ng m t Set.ế
N u th t trong vòng l p là r t quan tr ng khi duy t qua m t s u t p, hãy s d ng Tree... m t h ng v khácế ư ươ
c a s u t p, khi đó có s n. ư
Tránh s d ng Vector, tr khi b n c n kh năng đ ng b hóa c a nó.
Không nên lo l ng v vi c t i u hóa cho đ n khi (và tr khi) hi u năng tr thành m t v n đ . ư ế
Các b s u t p là m t trong nh ng khía c nh m nh m nh t c a ngôn ng Java. Đ ng ng i khi s d ng chúng, nh ng ư ư
c n c nh giác v các v "Tìm ra r i" (gotchas). Ví d , có m t cách thu n ti n đ chuy n đ i t m t Array thành m t
ArrayList:
Mã này đ a ra m t UnsupportedOperationException, vì List đ c Arrays.asList() tr v là không thay đ i đ c. B n khôngư ượ ượ
th thêm m t ph n t m i vào m t List không thay đ i. Hãy đ ý.
Theo IBM Vi t Nam