Class PriorityQueue<E>

PriorityQueue là một hàng đợi ưu tiên (priority queue) dựa trên cấu trúc priority heap, nơi các phần tử được sắp xếp theo thứ tự ưu tiên.

Một số đặc điểm nổi bật của PriorityQueue

↳ Sắp xếp theo thứ tự ưu tiên: Các phần tử trong PriorityQueue được sắp xếp dựa trên thứ tự tự nhiên (natural ordering) hoặc dựa trên một Comparator được cung cấp khi khởi tạo hàng đợi.

↳ Phần tử nhỏ nhất theo thứ tự ưu tiên sẽ luôn nằm ở đầu hàng đợi.

Không cho phép null: PriorityQueue không cho phép thêm phần tử null.

↳ Không đồng bộ: PriorityQueue không đồng bộ, tức là nếu nhiều luồng truy cập vào cùng một hàng đợi và có ít nhất một luồng thay đổi hàng đợi, thì cần sử dụng các biện pháp đồng bộ hóa bên ngoài hoặc sử dụng lớp thay thế an toàn luồng như PriorityBlockingQueue.

↳ Khả năng mở rộng: PriorityQueue không có giới hạn về số lượng phần tử, nhưng có dung lượng bên trong để quản lý kích thước của mảng được sử dụng để lưu trữ các phần tử trong hàng đợi. Khi các phần tử được thêm vào, dung lượng này sẽ tự động tăng lên khi cần thiết.

Hiệu suất:

↳ Các thao tác thêm, xóa phần tử (offer, poll, remove, add) có thời gian thực hiện là O(log(n)).

↳ Các thao tác kiểm tra phần tử có tồn tại (remove(Object), contains(Object)) có thời gian thực hiện là O(n).

↳ Các thao tác truy xuất phần tử đầu hàng đợi (peek, element, size) có thời gian thực hiện là O(1).

Khai báo Class PriorityQueue<E> trong Java

Để sử dụng Class PriorityQueue<E>, bạn cần import gói java.util vào đầu file Java của mình. Gói này cung cấp các lớp và giao diện để làm việc với các collection trong Java.

Cú pháp câu lệnh import:

Cú pháp

import java.util.PriorityQueue;

Cú pháp khai báo Class PriorityQueue<E>:

Cú pháp

public class PriorityQueue<E>
extends AbstractQueue<E>
implements Serializable

Dưới đây là giải thích chi tiết về cú pháp khai báo này:

↳ public: Là phạm vi truy cập - lớp PriorityQueue có thể được sử dụng ở bất kỳ đâu trong chương trình.

↳ class: Từ khóa để khai báo một lớp trong Java.

↳ PriorityQueue<E>: Tên lớp là PriorityQueue, có sử dụng generic với kiểu tham số E, tức là kiểu của các phần tử trong hàng đợi ưu tiên (ví dụ: PriorityQueue<String>).

↳ extends AbstractQueue<E>: PriorityQueue kế thừa từ lớp trừu tượng AbstractQueue, nghĩa là nó thừa hưởng các hành vi và phương thức cơ bản của một hàng đợi trong Java. AbstractQueue cung cấp các phương thức mặc định để quản lý hàng đợi, và PriorityQueue cung cấp thêm các tính năng sắp xếp ưu tiên.

↳ implements Serializable: PriorityQueue triển khai giao diện Serializable, nghĩa là các đối tượng của lớp này có thể được tuần tự hóa (serialization) để lưu trữ hoặc truyền qua mạng. Điều này rất hữu ích khi bạn cần lưu trữ trạng thái của hàng đợi hoặc truyền nó giữa các thành phần khác nhau của một ứng dụng.

Các constructor của lớp PriorityQueue

Khi làm việc với PriorityQueue, một cấu trúc dữ liệu đặc biệt giúp bạn quản lý các phần tử dựa trên mức độ ưu tiên của chúng, việc khởi tạo đúng cách là rất quan trọng. PriorityQueue cung cấp nhiều constructor linh hoạt để bạn có thể tùy chỉnh dung lượng ban đầu, cách sắp xếp (tự nhiên hoặc theo bộ so sánh tùy chỉnh), hoặc khởi tạo từ một tập hợp hiện có. Dưới đây là các constructor mà bạn có thể sử dụng:

↳ PriorityQueue(): Tạo một PriorityQueue rỗng với dung lượng ban đầu mặc định (11) và sắp xếp các phần tử theo thứ tự tự nhiên.

↳ PriorityQueue(Collection<? extends E> c): Tạo một PriorityQueue chứa các phần tử từ một bộ sưu tập khác.

↳ PriorityQueue(Comparator<? super E> comparator): Tạo một PriorityQueue với dung lượng ban đầu mặc định và sắp xếp các phần tử theo bộ so sánh đã cho.

↳ PriorityQueue(int initialCapacity): Tạo một PriorityQueue với dung lượng ban đầu được chỉ định và sắp xếp các phần tử theo thứ tự tự nhiên.

↳ PriorityQueue(int initialCapacity, Comparator<? super E> comparator): Tạo một PriorityQueue với dung lượng ban đầu được chỉ định và sắp xếp các phần tử theo bộ so sánh đã cho.

↳ PriorityQueue(PriorityQueue<? extends E> c): Tạo một PriorityQueue chứa các phần tử từ một PriorityQueue khác.

↳ PriorityQueue(SortedSet<? extends E> c): Tạo một PriorityQueue chứa các phần tử từ một sorted set khác.

Ví dụ

// Tạo một PriorityQueue rỗng với dung lượng mặc định và sắp xếp theo thứ tự tự nhiên
PriorityQueue<Integer> pq1 = new PriorityQueue<>();

// Tạo một PriorityQueue chứa các phần tử từ một List
List<String> words = Arrays.asList("banana", "apple", "orange");
PriorityQueue<String> pq2 = new PriorityQueue<>(words);

// Tạo một PriorityQueue sắp xếp theo thứ tự giảm dần
Comparator<Integer> descendingOrder = (a, b) -> b - a;
PriorityQueue<Integer> pq3 = new PriorityQueue<>(descendingOrder);

// Tạo một PriorityQueue với dung lượng ban đầu là 20
PriorityQueue<Double> pq4 = new PriorityQueue<>(20);

// Tạo một PriorityQueue với dung lượng ban đầu là 10 và sắp xếp theo thứ tự giảm dần
PriorityQueue<Character> pq5 = new PriorityQueue<>(10, descendingOrder);

// Tạo một PriorityQueue từ một PriorityQueue khác
PriorityQueue<Long> pq6 = new PriorityQueue<>(pq1); // pq1 phải chứa các phần tử kiểu Long hoặc có thể ép kiểu

// Tạo một PriorityQueue từ một SortedSet
Set<String> names = new TreeSet<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
PriorityQueue<String> pq7 = new PriorityQueue<>(names);

Lưu ý:

↳ PriorityQueue là một hàng đợi ưu tiên, nghĩa là các phần tử được sắp xếp theo thứ tự ưu tiên. Thứ tự ưu tiên có thể được xác định bởi thứ tự tự nhiên của các phần tử hoặc bởi một bộ so sánh được cung cấp.

↳ Khi tạo một PriorityQueue, bạn có thể chỉ định dung lượng ban đầu và bộ so sánh tùy chọn.

↳ Nếu không chỉ định bộ so sánh, các phần tử sẽ được sắp xếp theo thứ tự tự nhiên của chúng.

↳ Bạn có thể sử dụng các phương thức add(), remove(), peek(), poll() để thao tác với PriorityQueue.

Các phương thức chính trong lớp PriorityQueue

Lớp PriorityQueue cung cấp các phương thức để thao tác với một hàng đợi ưu tiên, bao gồm thêm, lấy, xóa các phần tử và kiểm tra kích thước của hàng đợi. Các phương thức add(), offer(), peek(), poll() đều liên quan đến việc thêm, lấy và xóa các phần tử trong priority queue, nhưng chúng có một số khác biệt nhỏ về cách hoạt động. Dưới đây là danh sách tất cả các phương thức của lớp PriorityQueue<E> trong Java:

↳ boolean add(E e): Thêm phần tử vào priority queue.

↳ boolean offer(E e): Thêm phần tử vào priority queue.

↳ void clear(): Xóa tất cả các phần tử khỏi priority queue.

↳ Comparator<? super E> comparator(): Trả về bộ so sánh được sử dụng để sắp xếp các phần tử.

↳ boolean contains(Object o): Kiểm tra xem priority queue có chứa phần tử đã cho hay không.

↳ Iterator<E> iterator(): Trả về một iterator duyệt qua các phần tử trong priority queue.

↳ E peek(): Lấy phần tử đầu của priority queue mà không xóa nó.

↳ E poll(): Lấy và xóa phần tử đầu của priority queue.

↳ boolean remove(Object o): Xóa một instance duy nhất của phần tử đã cho khỏi priority queue.

↳ int size(): Trả về số lượng phần tử trong priority queue.

↳ Spliterator<E> spliterator(): Tạo một Spliterator duyệt qua các phần tử trong priority queue.

↳ Object[] toArray(): Phương thức này trả về một mảng chứa tất cả các phần tử của hàng đợi. Mảng trả về có kiểu là Object[].

↳ <T> T[] toArray(T[] a): Phương thức này cũng trả về một mảng chứa tất cả các phần tử, nhưng kiểu mảng trả về sẽ là kiểu của mảng truyền vào.

Ví dụ về cách sử dụng các phương thức add(E e), offer(E e), peek(), và poll()

Dưới đây là các ví dụ về cách sử dụng các phương thức chính add(E e), offer(E e), peek(), và poll() của lớp PriorityQueue trong Java.

Ví dụ: Example.java

import java.util.PriorityQueue;

public class Example {
    public static void main(String[] args) {
        // Tạo một PriorityQueue với kiểu dữ liệu Integer
        PriorityQueue<Integer> pq = new PriorityQueue<>();

        // Thêm phần tử vào PriorityQueue bằng phương thức add() và offer()
        pq.add(10);
        pq.add(20);
        pq.add(15);
        pq.offer(30);
        pq.offer(5);

        // Hiển thị tất cả các phần tử trong PriorityQueue
        System.out.println("PriorityQueue sau khi thêm các phần tử:");
        for (Integer num : pq) {
            System.out.println(num);
        }

        // Lấy và xóa phần tử đầu của PriorityQueue bằng phương thức poll()
        System.out.println("\nPhần tử đầu của PriorityQueue được lấy và xóa: " + pq.poll());

        // Lấy phần tử đầu của PriorityQueue mà không xóa nó bằng phương thức peek()
        System.out.println("Phần tử đầu của PriorityQueue hiện tại là: " + pq.peek());

        // Xóa một phần tử cụ thể khỏi PriorityQueue
        pq.remove(20);

        // Hiển thị tất cả các phần tử còn lại trong PriorityQueue
        System.out.println("\nPriorityQueue sau khi xóa phần tử 20:");
        for (Integer num : pq) {
            System.out.println(num);
        }
    }
}

Kết quả của chương trình là:

get(ChronoField.DAY_OF_WEEK): 3
PriorityQueue sau khi thêm các phần tử:
5
10
15
30
20

Phần tử đầu của PriorityQueue được lấy và xóa: 5
Phần tử đầu của PriorityQueue hiện tại là: 10

PriorityQueue sau khi xóa phần tử 20:
10
30
15

Ví dụ về cách sử dụng các phương thức contains(Object o) và remove(Object o)

Dưới đây là ví dụ về cách sử dụng các phương thức contains(Object o) và remove(Object o) của lớp PriorityQueue với kiểu dữ liệu String trong một lớp Java.

Ví dụ: Example.java

import java.util.PriorityQueue;

public class Example {
    public static void main(String[] args) {
        // Tạo một PriorityQueue với kiểu dữ liệu String
        PriorityQueue<String> pq = new PriorityQueue<>();

        // Thêm các phần tử vào PriorityQueue
        pq.add("Apple");
        pq.add("Banana");
        pq.add("Cherry");
        pq.add("Date");

        // Kiểm tra xem PriorityQueue có chứa phần tử cụ thể hay không
        String elementToCheck = "Banana";
        if (pq.contains(elementToCheck)) {
            System.out.println("PriorityQueue chứa phần tử: " + elementToCheck);
        } else {
            System.out.println("PriorityQueue không chứa phần tử: " + elementToCheck);
        }

        // Xóa một instance cụ thể khỏi PriorityQueue
        String elementToRemove = "Cherry";
        if (pq.remove(elementToRemove)) {
            System.out.println("Đã xóa phần tử: " + elementToRemove);
        } else {
            System.out.println("Không tìm thấy phần tử để xóa: " + elementToRemove);
        }

        // Hiển thị tất cả các phần tử còn lại trong PriorityQueue
        System.out.println("\nPriorityQueue sau khi xóa phần tử Cherry:");
        for (String element : pq) {
            System.out.println(element);
        }
    }
}

Kết quả của chương trình là:

PriorityQueue chứa phần tử: Banana
Đã xóa phần tử: Cherry

PriorityQueue sau khi xóa phần tử Cherry:
Apple
Banana
Date

Ví dụ về cách sử dụng các phương thức comparator()

Dưới đây là ví dụ về cách sử dụng phương thức comparator() của lớp PriorityQueue trong Java. Phương thức comparator() trả về bộ so sánh (comparator) được sử dụng để sắp xếp các phần tử trong PriorityQueue. Nếu không có bộ so sánh được cung cấp khi tạo PriorityQueue, phương thức này sẽ trả về null, vì PriorityQueue sẽ sử dụng thứ tự tự nhiên của các phần tử.

Ví dụ: Example.java

import java.util.Comparator;
import java.util.PriorityQueue;

public class Example {
    public static void main(String[] args) {
        // Tạo một PriorityQueue với bộ so sánh cụ thể (sắp xếp theo thứ tự ngược)
        PriorityQueue<String> pq = new PriorityQueue<>(Comparator.reverseOrder());

        // Thêm các phần tử vào PriorityQueue
        pq.add("Apple");
        pq.add("Banana");
        pq.add("Cherry");
        pq.add("Date");

        // Lấy bộ so sánh được sử dụng để sắp xếp các phần tử
        Comparator<? super String> comparator = pq.comparator();
        if (comparator != null) {
            System.out.println("Bộ so sánh hiện tại của PriorityQueue là: " + comparator);
        } else {
            System.out.println("PriorityQueue không sử dụng bộ so sánh riêng (sử dụng thứ tự tự nhiên).");
        }

        // Hiển thị tất cả các phần tử trong PriorityQueue
        System.out.println("\nPriorityQueue:");
        while (!pq.isEmpty()) {
            System.out.println(pq.poll());  // Lấy và xóa phần tử đầu của PriorityQueue
        }
    }
}

Kết quả của chương trình là:

get(ChronoField.DAY_OF_WEEK): 3
Bộ so sánh hiện tại của PriorityQueue là: java.util.Collections$ReverseComparator@5305068a
PriorityQueue:
Date
Cherry
Banana
Apple

Ví dụ về cách sử dụng các phương thức iterator()

Dưới đây là ví dụ đơn giản về cách sử dụng phương thức iterator() của lớp PriorityQueue trong Java. Phương thức này trả về một Iterator cho phép bạn duyệt qua các phần tử trong PriorityQueue.

Ví dụ: Example.java

import java.util.PriorityQueue;
import java.util.Iterator;

public class Example {
    public static void main(String[] args) {
        // Tạo một PriorityQueue với kiểu dữ liệu String
        PriorityQueue<String> pq = new PriorityQueue<>();

        // Thêm các phần tử vào PriorityQueue
        pq.add("Apple");
        pq.add("Banana");
        pq.add("Cherry");
        pq.add("Date");

        // Lấy iterator và duyệt qua các phần tử
        Iterator<String> iterator = pq.iterator();
        System.out.println("Các phần tử trong PriorityQueue:");
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

Kết quả của chương trình là:

Các phần tử trong PriorityQueue:
Apple
Banana
Cherry
Date

Ví dụ về cách sử dụng các phương thức spliterator()

Dưới đây là ví dụ đơn giản về cách sử dụng phương thức spliterator() của lớp PriorityQueue trong Java. Phương thức spliterator() tạo một Spliterator cho phép bạn duyệt qua các phần tử của PriorityQueue. Spliterator() cung cấp các tính năng mở rộng hơn so với Iterator, bao gồm khả năng phân tách và xử lý các phần tử song song.

Ví dụ: Example.java

import java.util.PriorityQueue;
import java.util.Spliterator;
import java.util.function.Consumer;

public class Example {
    public static void main(String[] args) {
        // Tạo một PriorityQueue với kiểu dữ liệu String
        PriorityQueue<String> pq = new PriorityQueue<>();

        // Thêm các phần tử vào PriorityQueue
        pq.add("Apple");
        pq.add("Banana");
        pq.add("Cherry");
        pq.add("Date");

        // Lấy Spliterator và duyệt qua các phần tử
        Spliterator<String> spliterator = pq.spliterator();
        System.out.println("Các phần tử trong PriorityQueue:");
        spliterator.forEachRemaining(new Consumer<String>() {
            @Override
            public void accept(String element) {
                System.out.println(element);
            }
        });
    }
}

Kết quả của chương trình là:

Các phần tử trong PriorityQueue:
Apple
Banana
Cherry
Date

Ví dụ về cách sử dụng các phương thức toArray(T[] a)

Dưới đây là ví dụ đơn giản về cách sử dụng phương thức toArray(T[] a) của lớp PriorityQueue trong Java. Phương thức này chuyển đổi PriorityQueue thành một mảng với kiểu dữ liệu được chỉ định.

Ví dụ: Example.java

import java.util.PriorityQueue;

public class Example {
    public static void main(String[] args) {
        // Tạo một PriorityQueue với kiểu dữ liệu String
        PriorityQueue<String> pq = new PriorityQueue<>();

        // Thêm các phần tử vào PriorityQueue
        pq.add("Apple");
        pq.add("Banana");
        pq.add("Cherry");
        pq.add("Date");

        // Chuyển đổi PriorityQueue thành mảng
        // Tạo mảng với kích thước bằng kích thước của PriorityQueue
        String[] array = new String[pq.size()];
        pq.toArray(array);

        // Hiển thị các phần tử trong mảng
        System.out.println("Các phần tử trong mảng:");
        for (String element : array) {
            System.out.println(element);
        }
    }
}

Kết quả của chương trình là:

Các phần tử trong mảng:
Apple
Banana
Cherry
Date

Ví dụ PriorityQueue với đối tượng do người dùng định nghĩa

Dưới đây là ví dụ về cách sử dụng PriorityQueue với đối tượng do người dùng định nghĩa trong Java. Trong ví dụ này, chúng ta sẽ tạo một lớp đối tượng tùy chỉnh và sử dụng PriorityQueue để lưu trữ và quản lý các đối tượng này.

Ví dụ: Example.java

import java.util.PriorityQueue;

// Định nghĩa lớp Person với các thuộc tính và phương thức so sánh
class Person implements Comparable<Person> {
    String name;
    int age;

    // Constructor
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter cho tên và tuổi
    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    // Phương thức toString để hiển thị thông tin của Person
    @Override
    public String toString() {
        return name + " (" + age + " tuổi)";
    }

    // Phương thức compareTo để so sánh hai đối tượng Person theo tuổi
    @Override
    public int compareTo(Person other) {
        return Integer.compare(this.age, other.age);
    }
}

public class Example {
    public static void main(String[] args) {
        // Tạo PriorityQueue với đối tượng Person, sắp xếp theo tuổi
        PriorityQueue<Person> pq = new PriorityQueue<>();

        // Thêm các đối tượng Person vào PriorityQueue
        pq.add(new Person("Alice", 30));
        pq.add(new Person("Bob", 25));
        pq.add(new Person("Charlie", 35));
        pq.add(new Person("Diana", 28));

        // Hiển thị tất cả các đối tượng trong PriorityQueue
        System.out.println("Danh sách các đối tượng trong PriorityQueue (sắp xếp theo tuổi):");
        while (!pq.isEmpty()) {
            System.out.println(pq.poll());
        }
    }
}

Kết quả của chương trình là:

Danh sách các đối tượng trong PriorityQueue (sắp xếp theo tuổi):
Bob (25 tuổi)
Diana (28 tuổi)
Alice (30 tuổi)
Charlie (35 tuổi)

Vậy là chúng ta đã tìm hiểu các phương thức chính của lớp PriorityQueue. Với khả năng tự động sắp xếp và xử lý các phần tử dựa trên ưu tiên, cùng với sự linh hoạt giữa các phương thức như add(), offer(), peek(), và poll(), PriorityQueue là một công cụ mạnh mẽ để quản lý các hàng đợi mà thứ tự xử lý phụ thuộc vào giá trị của phần tử. Nắm vững PriorityQueue sẽ giúp bạn giải quyết hiệu quả nhiều bài toán liên quan đến việc ưu tiên hóa dữ liệu trong ứng dụng Java của mình.

Câu Nói Truyền Cảm Hứng

“Bắt đầu ở đâu không quan trọng, quan trọng là bạn sẵn sàng bắt đầu.” – W. Clement Stone

Không Gian Tích Cực

“Chúc bạn luôn giữ vững niềm tin và sức mạnh để vượt qua mọi thử thách trong cuộc sống.”