Java - cấu trúc dữ liệu

Chào mừng các bạnfuture programmers! Hôm nay, chúng ta sẽ cùng bước vào thế giới đầy thú vị của cấu trúc dữ liệu Java. Là một giáo viên khoa học máy tính gần gũi, tôi sẽ hướng dẫn các bạn qua hành trình này, từng bước một. Đừng lo lắng nếu bạn mới bắt đầu học lập trình - chúng ta sẽ bắt đầu từ những điều cơ bản và dần dần nâng cao. Vậy, hãy mang theo mũ bảo hiểm ảo của bạn, và chúng ta cùng bắt đầu xây dựng kiến thức nhé!

Java - Data Structures

Giới thiệu về cấu trúc dữ liệu

Trước khi chúng ta nhảy vào các cấu trúc dữ liệu cụ thể trong Java, hãy cùng hiểu về cấu trúc dữ liệu là gì và tại sao chúng lại quan trọng.

Hãy tưởng tượng bạn đang tổ chức một thư viện. Bạn sẽ không chỉ đơn giản là掷 tất cả sách vào một đống, đúng không? Tất nhiên là không! Bạn sẽ sắp xếp chúng một cách hợp lý để dễ dàng tìm kiếm và quản lý. Đó chính xác là điều mà cấu trúc dữ liệu làm cho dữ liệu của chúng ta trong lập trình.

Cấu trúc dữ liệu là các cách tổ chức và lưu trữ dữ liệu để chúng ta có thể truy cập và sửa đổi chúng một cách hiệu quả. Trong Java, chúng ta có nhiều cấu trúc dữ liệu内置 mà chúng ta có thể sử dụng, mỗi cấu trúc có những ưu điểm và trường hợp sử dụng riêng.

Cấu trúc dữ liệu内置 trong Java

Hãy cùng khám phá một số cấu trúc dữ liệu phổ biến trong Java:

Enumeration

Enumeration giống như một máy bán vé phát số thứ tự. Nó là một giao diện trong Java cho phép chúng ta truy cập các phần tử trong một bộ thu thập một lần một.

Dưới đây là một ví dụ đơn giản:

import java.util.*;

public class EnumerationExample {
public static void main(String args[]) {
Vector<String> dayNames = new Vector<>();
dayNames.add("Monday");
dayNames.add("Tuesday");
dayNames.add("Wednesday");

Enumeration<String> days = dayNames.elements();

while (days.hasMoreElements()) {
System.out.println(days.nextElement());
}
}
}

Trong ví dụ này, chúng ta tạo một Vector các tên ngày và sử dụng Enumeration để duyệt qua chúng. Phương thức hasMoreElements() kiểm tra xem có phần tử tiếp theo hay không, và nextElement() lấy phần tử tiếp theo.

BitSet

BitSet giống như một hàng các công tắc ánh sáng - mỗi cái có thể bật (1) hoặc tắt (0). Nó rất hữu ích khi bạn cần lưu trữ một loạt các giá trị true/false một cách hiệu quả.

Dưới đây là một ví dụ:

import java.util.BitSet;

public class BitSetExample {
public static void main(String args[]) {
BitSet bits1 = new BitSet(16);
BitSet bits2 = new BitSet(16);

// đặt một số bit
for(int i = 0; i < 16; i++) {
if((i % 2) == 0) bits1.set(i);
if((i % 5) != 0) bits2.set(i);
}

System.out.println("Mẫu ban đầu trong bits1: " + bits1);
System.out.println("Mẫu ban đầu trong bits2: " + bits2);

// AND bits
bits2.and(bits1);
System.out.println("bits2 AND bits1: " + bits2);
}
}

Ví dụ này minh họa việc tạo BitSets, đặt bit và thực hiện các phép toán bit-wise.

Vector

Vector giống như một mảng ma thuật có thể tăng hoặc giảm kích thước khi cần. Nó tương tự như ArrayList nhưng được đồng bộ hóa, làm cho nó an toàn với thread.

Dưới đây là cách bạn có thể sử dụng một Vector:

import java.util.*;

public class VectorExample {
public static void main(String args[]) {
Vector<Integer> vec = new Vector<>(3, 2);
System.out.println("Kích thước ban đầu: " + vec.size());
System.out.println("Dung lượng ban đầu: " + vec.capacity());

vec.addElement(1);
vec.addElement(2);
vec.addElement(3);
vec.addElement(4);
System.out.println("Dung lượng sau bốn lần thêm: " + vec.capacity());

vec.addElement(5);
System.out.println("Dung lượng hiện tại: " + vec.capacity());

System.out.println("Phần tử đầu tiên: " + vec.firstElement());
System.out.println("Phần tử cuối cùng: " + vec.lastElement());
}
}

Ví dụ này cho thấy cách tạo một Vector, thêm phần tử và kiểm tra kích thước và dung lượng của nó.

Stack

Stack giống như một chồng đĩa - bạn chỉ có thể thêm hoặc loại bỏ từ trên cùng. Nó tuân theo nguyên tắc Last-In-First-Out (LIFO).

Hãy xem Stack hoạt động như thế nào:

import java.util.*;

public class StackExample {
public static void main(String args[]) {
Stack<String> stack = new Stack<>();
stack.push("Bottom");
stack.push("Middle");
stack.push("Top");

System.out.println("Stack: " + stack);
System.out.println("Đã bóc: " + stack.pop());
System.out.println("Stack sau khi bóc: " + stack);
System.out.println("Xem: " + stack.peek());
System.out.println("Stack sau khi xem: " + stack);
}
}

Ví dụ này minh họa việc đẩy phần tử vào stack, bóc chúng ra và xem phần tử trên cùng.

Dictionary

Dictionary là một lớp trừu tượng đại diện cho cấu trúc dữ liệu key-value. Nó giống như một từ điển thực tế, nơi mỗi từ (key) có một định nghĩa (value).

Mặc dù Dictionary là trừu tượng và không thể khởi tạo trực tiếp, nhưng lớp con Hashtable của nó thường được sử dụng:

import java.util.*;

public class DictionaryExample {
public static void main(String args[]) {
Dictionary<String, String> dict = new Hashtable<>();

dict.put("Apple", "Một quả");
dict.put("Java", "Một ngôn ngữ lập trình");
dict.put("Computer", "Một thiết bị điện tử");

System.out.println("Dictionary: " + dict);
System.out.println("Giá trị cho key 'Java': " + dict.get("Java"));

System.out.println("Keys: ");
for (Enumeration<String> keys = dict.keys(); keys.hasMoreElements();) {
System.out.println(keys.nextElement());
}
}
}

Ví dụ này cho thấy cách sử dụng Dictionary (qua Hashtable) để lưu trữ và truy xuất các cặp key-value.

Hashtable

Hashtable giống như một tủ tài liệu siêu hiệu quả. Nó lưu trữ các cặp key-value và cho phép truy xuất nhanh chóng các giá trị dựa trên keys của chúng.

Dưới đây là một ví dụ sử dụng Hashtable:

import java.util.*;

public class HashtableExample {
public static void main(String args[]) {
Hashtable<String, Integer> numbers = new Hashtable<>();
numbers.put("one", 1);
numbers.put("two", 2);
numbers.put("three", 3);

System.out.println("Hashtable: " + numbers);
System.out.println("Giá trị của 'two': " + numbers.get("two"));
System.out.println("Có phải 'four' là một key? " + numbers.containsKey("four"));
System.out.println("Có phải 3 là một giá trị? " + numbers.containsValue(3));

numbers.remove("two");
System.out.println("Hashtable sau khi loại bỏ 'two': " + numbers);
}
}

Ví dụ này minh họa việc thêm các cặp key-value vào Hashtable, truy xuất giá trị, kiểm tra keys và values, và loại bỏ các mục.

Properties

Properties là một loại Hashtable đặc biệt được thiết kế để lưu trữ các cặp key-value chuỗi. Nó thường được sử dụng cho các thiết lập cấu hình.

Hãy xem Properties hoạt động như thế nào:

import java.util.*;

public class PropertiesExample {
public static void main(String args[]) {
Properties capitals = new Properties();
capitals.put("USA", "Washington D.C.");
capitals.put("France", "Paris");
capitals.put("Japan", "Tokyo");

System.out.println("Properties: " + capitals);
System.out.println(" Thủ đô của Pháp: " + capitals.getProperty("France"));

// Đặt một giá trị mặc định
System.out.println(" Thủ đô của Tây Ban Nha: " + capitals.getProperty("Spain", "Không tìm thấy"));

capitals.list(System.out);
}
}

Ví dụ này cho thấy cách sử dụng Properties để lưu trữ và truy xuất các cặp key-value chuỗi, với một giá trị mặc định cho các keys bị thiếu.

Kết luận

Chúc mừng! Bạn đã vừa bước những bước đầu tiên vào thế giới của cấu trúc dữ liệu Java. Mỗi cấu trúc trong số này có những tính chất và trường hợp sử dụng riêng. Trong hành trình lập trình của bạn, bạn sẽ thấy mình sử dụng các cấu trúc khác nhau tùy thuộc vào nhu cầu cụ thể của bạn.

Nhớ rằng, việc chọn đúng cấu trúc dữ liệu có thể làm cho chương trình của bạn chạy hiệu quả hơn. Điều này giống như việc chọn đúng công cụ cho công việc - một cái búa rất tốt cho đinh, nhưng không tốt cho ốc vít!

Tiếp tục tập luyện với các cấu trúc này, thử sử dụng chúng trong các dự án của riêng bạn và đừng ngần ngại thử nghiệm. Chúc bạn may mắn với lập trình!

Credits: Image by storyset