Các kiểu cấu trúc dữ liệu tổ hợp như Array
, Map
, Set
, v.v... được Java
gom vào package java.util
và gọi chung là Collections Framework
- một bộ khung dựng sẵn cho giải pháp lưu trữ và xử lý dữ liệu tập hợp trong môi trường vận hành. Và theo như tài liệu do Oracle
cung cấp thì các lập trình viên khi sử dụng Framework
dựng sẵn này nên chú ý tới các thành phần như sau:
- Các
interface
thể hiện giao diện lập trình mà chúng ta có thể tương tác qua đó. Ví dụ:Collection
,List
, v.v... - Các
class
triển khai các cấu trúc dữ liệu cụ thể. Ví dụ:ArrayList
,LinkedList
, v.v... - Các phương thức giải thuật
algorithm
đã được triển khai vào cácclass
cấu trúc dữ liệu ở trên để hỗ trợ tối ưu hiệu năng của các thao tác chỉnh sửa, sắp xếp, và tìm kiếm dữ liệu.
Như thường lệ thì mình sẽ bỏ qua hạng mục cuối cùng mang tính chất tối ưu hóa hiệu năng do bản thân chưa có ý định xây dựng ứng dụng có cấp độ phức tạp tới mức cần thiết phải quan tâm tới yếu tố này. Ở đây chúng ta sẽ điểm danh qua các interface
và các class
triển khai của Java Collections Framework
để có cái nhìn tổng quát về tên và trường hợp ứng dụng.
I/ Interface
java.util.Collection
- làinterface
đầu tiên củaCollections Framework
và là nền tảng của cácinterface
khác.Collection
biểu thị cho một cấu trúc lưu trữ bất kỳ có khả năng chứa cácobject
dữ liệu có khả năng trùng lặp hoặc không.Collection
mở rộngjava.lang.Iterable
để biểu thị chức năng hỗ trợ thao tác lặp qua tậpobject
dữ liệu lưu trữ bằng các phương thức đặc trưng.
List
java.util.List
- mở rộngCollection
và biểu thị một cấu trúc dữ liệu tập hợp có thứ tựordered-list
có khả năng trùng lặp dữ liệu. Vì vậy nênList
trongJava
có hỗ trợ truy xuất cácobject
dữ liệu bằng các trị số chỉ vị tríindex
.java.util.Queue
- mở rộngCollection
và biểu thị cho cấu trúc dữ liệu tập hợp ở dạng danh sách, hỗ trợ thao tác thêm dữ liệu ở một đầu và xóa dữ liệu ở đầu còn lại của danh sách.java.util.Deque
- mở rộngQueue
và biểu thị cho cấu trúc dữ liệu tập hợp ở dạng danh sách, hỗ trợ các thao tác thêm hoặc xóa dữ liệu ở cả hai đầu của danh sách.
Set
java.util.Set
- mở rộngCollection
và biểu thị một cấu trúc dữ liệu tập hợp không có dữ liệu trùng lặp.java.util.SortedSet
- mở rộngSet
và đảm bảo rằng tập hợp dữ liệu luôn được sắp xếp theo tiêu chí được cung cấp bởijava.util.Comparator
.java.util.NavigableSet
- mở rộngCollection
vàSortedSet
, biểu thị cho cấu trúcSet
có cung cấp phương thức để di chuyển từ vị trí mộtobject
dữ liệu đang lưu trữ tớiobject
dữ liệu tiếp theo.
Map
java.util.Map
- biểu thị cho cấu trúc lưu trữ tập hợp dữ liệu bằng các bản ghiEntry
có dạngKey/Value
.java.util.Map.Entry
- biểu thị cho các bản ghi củaMap
.
java.util.SortedMap
- giống vớiSortedSet
so vớiSet
.java.util.NavigableMap
- mở rộngMap
vàSortedMap
, biểu thị cho cấu trúcMap
có cung cấp phương thức để di chuyển từ vị trí mộtobject
dữ liệu đang lưu trữ tớiobject
dữ liệu tiếp theo.
Enum
java.util.Enumeration
- biểu thị mộtobject
dữ liệu trong tập hợp dạng thẻ. Chỉ hiển thị duy nhất một thẻ tại mỗi thời điểm.
Tham khảo
java.io.Serializeable
- có thể được sử dụng cho thao tác đọc/ghi các tệp hoặc các kênh nhập/xuất.java.lang.Cloneable
- có thể thực hiện phép nhân bản và tạo ra bản saoclone
hoàn toàn độc lập.java.util.RandomAccess
- biểu thị cho tập hợp dữ liệu có hỗ trợ nhiều phương thức truy xuất dữ liệu khác nhau, ví dụ như thông qua trị số chỉ vị tríindex
hoặc thông qua trình lặpinterator
.
II/ Class
java.util.AbstractCollection
- triển khaiinterface Collection
, tạo nền tảng cho cácclass
triển khai các cấu trúc dữ liệu khác nhau.java.util.Collections
- các phương thứcstatic
hỗ trợ làm việc với các kiểuCollection
.java.util.Arrays
- các phương thứcstatic
hỗ trợ làm việc với các mảng đơn thuầnliteral array
.
List
java.util.AbstractList
- mở rộngAbstractCollection
và triển khaiinterface List
, tạo nền tảng cho cácclass
triển khai cấu trúcList
.java.util.AbstractSequentialList
- mở rộngAbstractList
, tạo nền tảng cho cácclass
triển khai cấu trúcList
.java.util.ArrayList
- mở rộngAbstractList
và triển khai thêm cácinterface
bao gồm:java.io.Serializeable
,java.lang.Cloneable
,java.util.Deque
,java.util.Queue
,java.util.RandomAccess
.java.util.LinkedList
- mở rộngAbstractSequentialList
và triển khai thêm cácinterface
bao gồm:java.io.Serializeable
,java.lang.Cloneable
,java.util.Deque
,java.util.Queue
,java.util.RandomAccess
.
Khác biệt căn bản giữa ArrayList
và LinkedList
là ở cách quản lý bộ nhớ của cấu trúc lưu trữ nội tại. ArrayList
sử dụng kiểu mảng đơn thuần và sẽ thực hiện xin cấp phát lại bộ nhớ để có mảng rộng hơn khi hết dung lượng lưu trữ, còn LinkedList
sử dụng kiểu cấp phát bộ nhớ rời rạc với các object
vỏ bọc dữ liệu liên kết với nhau bằng các kết nối tham chiếu.
java.util.Vector
- tên gọi khác củaArrayList
, đượcJava
sử dụng để biểu thị phiên bảnthread-safe
.java.util.Stack
- mở rộngVector
và biểu thị tập dữ liệu với thao tác thêm/bỏ với trình tựLIFO - Last-In-First-Out
. Phần tử nào thêm vào cuối cùng thì sẽ là phần tử được lấy ra đầu tiên.
Set
java.util.AbstractSet
- mở rộngAbstractCollection
và triển khaiinterface Set
, tạo nền tảng cho cácclass
triển khai cấu trúcSet
.java.util.HashSet
- mở rộngAbstractSet
và triển khai thêm cácinterface
bao gồm:java.io.Serializable
,java.lang.Cloneable
.java.util.LinkedHashSet
- mở rộngHashSet
.java.util.TreeSet
- mở rộngAbstractSet
và triển khai thêm cácinterface
bao gồm:java.io.Serializeable
,java.lang.Cloneable
,java.util.NavigableSet
,java.util.SortedSet
.
Tương quan giữa HashSet
và LinkedHashSet
có thể so sánh với ArrayList
và LinkedList
. Ở đây có thêm TreeSet
, tổ chức lưu trữ rời rạc giống với LinkedSet
nhưng các object
vỏ bọc dữ liệu liên kết với nhau theo mô hình cây nhị phân.
Map
java.util.AbstractMap
- triển khaiinterface Map
, tạo nền tảng cho cácclass
triển khai cấu trúcMap
.java.util.HashMap
- mở rộngAbstractMap
và triển khai thêm cácinterface
bao gồm:java.io.Serializeable
,java.lang.Cloneable
.java.util.LinkedHashMap
- mở rộngHashMap
.java.util.TreeMap
- mở rộngAbstractMap
và triển khai thêm cácinterface
bao gồm:java.io.Serializeable
,java.lang.Cloneable
,java.util.NavigableMap
,java.util.SortedMap
.
Ở đây tương quan giữa HashMap
, LinkedHashMap
, và TreeMap
, cũng giống với trường hợp của HashSet
, LinkedHashSet
, và TreeSet
.
java.util.Dictionary
- tên gọi khác củaMap
đượcJava
sử dụng để biểu thị phiên bảnthread-safe
.java.util.HashTable
- mở rộngDictionary
và triển khai thêm cácinterface
bao gồm:java.util.Map
,java.lang.Cloneable
,java.io.Serializable
.java.util.Properties
- mở rộngHashTable
và hỗ trợ sử dụng làm nguồn đọc/ghi cho các kênh nhập/xuấtStandard IO
.
Và cuối cùng là hai trường hợp triển khai đặc biệt của HashMap
:
java.util.WeakHashMap
- mở rộngAbstractMap
và biểu thị cấu trúcMap
không ràng buộc cácKey
bằng liên kết chắc chắn. Khi cácobject
ở vị trí cácKey
không còn được sử dụng bởi các nơi khác trong chương trình,WeakHashMap
sẽ không ngănJVM
tự động xóa cácobject
này và bản ghi tương ứng trongWeakHashMap
cũng sẽ tự động được xóa.java.util.IdentityHashMap
- mở rộngAbstractMap
và biểu thị cấu trúcMap
sử dụng logic so sánh giữa các bản ghi bằng các địa chỉ tham chiếu.
III/ Usage
Việc sử dụng bộ framework
này cũng không có gì quá đặc biệt để lưu ý. Chỉ đơn giản là ở thời điểm cần chọn một cấu trúc lưu trữ dữ liệu tập hợp thì chúng ta xem lướt qua danh sách giới thiệu tổng quan ở đây để chọn ra một class
được mô tả phù hợp và xem tên của các phương thức được cung cấp trong liên kết đính kèm.
Tuy nhiên, hãy để mình giả định là bạn mới chỉ có nền tảng ở mức căn bản nhất mà mình nói đến ở bài viết đầu tiên của Sub-Series này. Đó là đã kha khá quen với việc sử dụng các cấu trúc lệnh Imperative
và chỉ mới sử dụng một ngôn ngữ định kiểu tĩnh static-typing
đầu tiên là C
. Như vậy khi bắt đầu sử dụng Java
- một ngôn ngữ hỗ trợ các kiểu dữ liệu trừu tượng như interface
, abstract class
, và đồng thời hỗ trợ Generic Programming
ở cấp độ cú pháp của ngôn ngữ - thì đây là thời điểm phù hợp để làm quen với việc sử dụng các tên định kiểu này.
Và vì vậy nên ở đây chúng ta sẽ có một vài đoạn code ví dụ nhỏ, có thể không hẳn là cần thiết đối với việc chạy thử tính năng của các phương thức được cung cấp trong tài liệu, nhưng lại rất cần thiết để chúng ta làm quen với mindset
của các lập trình viên OOP
.
import java.util.*;
import java.io.*; class App { public static void main (String[] $args) { List<String> $wordList = new ArrayList<> (); // -- $wordList.add ("the"); $wordList.add ("quick"); $wordList.add ("brown"); $wordList.add ("fox"); $wordList.add ("jumps"); $wordList.add ("over"); $wordList.add ("the"); $wordList.add ("lazy"); $wordList.add ("dog"); // -- App.<String>printList ($wordList); } public static <Type> void printList (List<Type> $list) { $list.forEach (($element) -> System.out.println ($element)); } } // -- end class
Ở đây chúng ta có một ví dụ đơn giản là việc chọn ra một kiểu Collection
bất kỳ để lưu trữ các chuỗi có thể trùng lặp hoặc không. Sau đó yêu cầu là cần thiết kế một phương thức printList
để in nội dung của một List
bất kỳ. Thao tác định kiểu mà chúng ta đang sử dụng ở đây là thông qua interface List
thay vì một class
cụ thể triển khai interface
này.
Tại sao nên làm như vậy?
Thao tác sử dụng các tên định kiểu trừu tượng như thế này là rất phổ biến trong Java
. Mục tiêu là để code của chúng ta trở nên linh động hơn và một phương thức như printList
sẽ có thể áp dụng cho nhiều trường hợp nhất có thể.
Một object
bất kỳ xuất hiện trong logic hoạt động của chương trình luôn có thể là một thiết kế đa năng với nhiều nhóm thuộc tính và phương thức, hay nói cách khác là nhiều giao diện tương tác interface
. Nếu như chúng ta đặt vào đó tên định kiểu của một class
cụ thể thì trình biên dịch sẽ yêu cầu object
nhận được ở vị trí đó cần triển khai ít nhất là đầy đủ các giao diện tương tác mà class
đó định nghĩa. Và như vậy sẽ giảm thiểu khả năng áp dụng lại phương thức printList
cho các class
khác không thuộc nhánh kế thừa từ class
đó.
Khi viết một đoạn code hay một phương thức bất kỳ, chúng ta nên luôn luôn cố gắng chọn sử dụng các tên định kiểu dữ liệu đầu vào với nhiều khả năng nhất để code hoạt động được, thay vì luôn luôn chọn sử dụng tên của các
class
cụ thể.
[Object-Oriented + Java] Bài viết #8 - Concurency Programming