- vừa được xem lúc

[Object-Oriented + Java] Bài viết #7 - Java Collections Framework

0 0 17

Người đăng: Semi Dev

Theo Viblo Asia

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ác class 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ủa Collections Framework và là nền tảng của các interface 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ác object dữ liệu có khả năng trùng lặp hoặc không. Collection mở rộng java.lang.Iterable để biểu thị chức năng hỗ trợ thao tác lặp qua tập object dữ liệu lưu trữ bằng các phương thức đặc trưng.

List

  • java.util.List - mở rộng Collection 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ên List trong Java có hỗ trợ truy xuất các object dữ liệu bằng các trị số chỉ vị trí index.
  • java.util.Queue - mở rộng Collection 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ộng Queue 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ộng Collection 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ộng Set 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ởi java.util.Comparator.
  • java.util.NavigableSet - mở rộng CollectionSortedSet, biểu thị cho cấu trúc Set có cung cấp phương thức để di chuyển từ vị trí một object dữ liệu đang lưu trữ tới object 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 ghi Entry có dạng Key/Value.
  • java.util.SortedMap - giống với SortedSet so với Set.
  • java.util.NavigableMap - mở rộng MapSortedMap, biểu thị cho cấu trúc Map có cung cấp phương thức để di chuyển từ vị trí một object dữ liệu đang lưu trữ tới object dữ liệu tiếp theo.

Enum

  • java.util.Enumeration - biểu thị một object 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 sao clone 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ặp interator.

II/ Class

  • java.util.AbstractCollection - triển khai interface Collection, tạo nền tảng cho các class triển khai các cấu trúc dữ liệu khác nhau.
  • java.util.Collections - các phương thức static hỗ trợ làm việc với các kiểu Collection.
  • java.util.Arrays - các phương thức static hỗ trợ làm việc với các mảng đơn thuần literal array.

List

  • java.util.AbstractList - mở rộng AbstractCollection và triển khai interface List, tạo nền tảng cho các class triển khai cấu trúc List.
  • java.util.AbstractSequentialList - mở rộng AbstractList, tạo nền tảng cho các class triển khai cấu trúc List.
  • java.util.ArrayList - mở rộng AbstractList và triển khai thêm các interface bao gồm: java.io.Serializeable, java.lang.Cloneable, java.util.Deque, java.util.Queue, java.util.RandomAccess.
  • java.util.LinkedList - mở rộng AbstractSequentialList và triển khai thêm các interface 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 ArrayListLinkedList 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ủa ArrayList, được Java sử dụng để biểu thị phiên bản thread-safe.
  • java.util.Stack - mở rộng Vector 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ộng AbstractCollection và triển khai interface Set, tạo nền tảng cho các class triển khai cấu trúc Set.
  • java.util.HashSet - mở rộng AbstractSet và triển khai thêm các interface bao gồm: java.io.Serializable, java.lang.Cloneable.
  • java.util.LinkedHashSet - mở rộng HashSet.
  • java.util.TreeSet - mở rộng AbstractSet và triển khai thêm các interface bao gồm: java.io.Serializeable, java.lang.Cloneable, java.util.NavigableSet, java.util.SortedSet.

Tương quan giữa HashSetLinkedHashSet có thể so sánh với ArrayListLinkedList. Ở đâ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 khai interface Map, tạo nền tảng cho các class triển khai cấu trúc Map.
  • java.util.HashMap - mở rộng AbstractMap và triển khai thêm các interface bao gồm: java.io.Serializeable, java.lang.Cloneable.
  • java.util.LinkedHashMap - mở rộng HashMap.
  • java.util.TreeMap - mở rộng AbstractMap và triển khai thêm các interface 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ủa Map được Java sử dụng để biểu thị phiên bản thread-safe.
  • java.util.HashTable - mở rộng Dictionary và triển khai thêm các interface bao gồm: java.util.Map, java.lang.Cloneable, java.io.Serializable.
  • java.util.Properties - mở rộng HashTable và hỗ trợ sử dụng làm nguồn đọc/ghi cho các kênh nhập/xuất Standard 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ộng AbstractMap và biểu thị cấu trúc Map không ràng buộc các Key bằng liên kết chắc chắn. Khi các object ở vị trí các Key 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ăn JVM tự động xóa các object này và bản ghi tương ứng trong WeakHashMap cũng sẽ tự động được xóa.
  • java.util.IdentityHashMap - mở rộng AbstractMap và biểu thị cấu trúc Map 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

Bình luận

Bài viết tương tự

- vừa được xem lúc

Closure trong Javascript - Phần 2: Định nghĩa và cách dùng

Các bạn có thể đọc qua phần 1 ở đây. Để mọi người không quên, mình xin tóm tắt gọn lại khái niệm lexical environment:.

0 0 51

- vừa được xem lúc

Var vs let vs const? Các cách khai báo biến và hằng trong Javascript

Dạo này mình tập tành học Javascript, thấy có 2 cách khai báo biến khác nhau nên đã tìm tòi sự khác biệt. Nay xin đăng lên đây để mọi người đọc xong hy vọng phân biệt được giữa let và var, và sau đó là khai báo hằng bằng const.

0 0 31

- vừa được xem lúc

VueJS: Tính năng Mixins

Chào mọi người, hôm nay mình sẽ viết về Mixins và 1 số vấn đề trong sử dụng Mixins hay ho mà mình gặp trong dự án thực. Trích dẫn từ trang chủ của VueJS:.

0 0 27

- vừa được xem lúc

Asset Pipeline là cái chi chi?

Asset Pipeline. Asset pipeline là cái chi chi. . Giải thích:.

0 0 47

- vừa được xem lúc

Tạo data table web app lấy dữ liệu từ Google Sheets sử dụng Apps Script

Google Sheets là công cụ tuyệt vời để lưu trữ bảng tính trực tuyến, bạn có thể truy cập bảng tính bất kỳ lúc nào ở bất kỳ đâu và luôn sẵn sàng để chia sẻ với người khác. Bài này gồm 2 phần.

0 0 266

- vừa được xem lúc

Học Deep Learning trên Coursera miễn phí

Bạn muốn bắt đầu với Deep Learning nhưng không biết bắt đầu từ đâu? Bạn muốn có một công việc ở mức fresher về Deep Learning? Bạn muốn khoe bạn bè về kiến thức Deep Learning của mình. Bắt đầu từ đâu.

0 0 35