Class ArrayDeque<E>

ArrayDeque là một triển khai linh hoạt và hiệu quả của giao diện Deque (Double-ended Queue), được xây dựng trên nền tảng của mảng động có khả năng tự động thay đổi kích thước.

ArrayDeque không có giới hạn về dung lượng, nó tự động mở rộng khi cần thiết để hỗ trợ việc sử dụng.

Một số đặc điểm nổi bật của ArrayDeque<E>

↳ Không đồng bộ: ArrayDeque không đồng bộ, nghĩa là nó không hỗ trợ truy cập đồng thời bởi nhiều luồng nếu không có đồng bộ hóa bên ngoài. Do đó, không nên sử dụng ArrayDeque trong các tình huống đồng thời mà không có cơ chế đồng bộ hóa.

↳ Các phần tử null không được phép trong ArrayDeque.

↳ Nếu bạn dùng iterator để duyệt ArrayDeque và thay đổi cấu trúc deque trong lúc duyệt (trừ khi dùng chính iterator.remove()), chương trình sẽ lập tức báo lỗi ConcurrentModificationException. Tuy nhiên, trong môi trường nhiều luồng mà không đồng bộ hóa, thì lỗi này không được đảm bảo xảy ra, chỉ phát hiện trên cơ sở "cố gắng tốt nhất".

Hiệu suất:

↳ Hầu hết các thao tác trên ArrayDeque hoạt động trong thời gian hằng số (amortized constant time).

↳ Các thao tác như xóa phần tử (remove), xóa phần tử đầu tiên (removeFirstOccurrence), xóa phần tử cuối cùng (removeLastOccurrence), kiểm tra sự tồn tại của phần tử (contains), xóa phần tử thông qua iterator (iterator.remove()), và các thao tác hàng loạt (bulk operations) có thời gian thực hiện là O(n).

Khai báo Class ArrayDeque<E> trong Java

Để sử dụng Class ArrayDeque<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.ArrayDeque;

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

Cú Pháp

public class ArrayDeque<E>
extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable

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

↳ public: Lớp ArrayDeque có thể được truy cập từ bất kỳ đâu trong chương trình.

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

↳ ArrayDeque<E>: Tên lớp là ArrayDeque có sử dụng generic type E, đại diện cho kiểu phần tử trong hàng đợi.

↳ extends AbstractCollection<E>: Lớp ArrayDeque kế thừa từ lớp AbstractCollection, điều này có nghĩa là ArrayDeque được thừa hưởng các phương thức và thuộc tính từ AbstractCollection, một lớp cơ bản cung cấp những cài đặt mặc định cho một số phương thức của giao diện Collection.

↳ implements Deque<E>: Lớp ArrayDeque triển khai giao diện Deque, điều này có nghĩa là ArrayDeque cung cấp các phương thức cần thiết để hoạt động như một deque (double-ended queue - hàng đợi hai đầu). Deque là một giao diện con của Queue và nó cho phép thao tác chèn và xóa phần tử từ cả hai đầu của hàng đợi.

↳ implements Cloneable: Lớp ArrayDeque cũng triển khai giao diện Cloneable, nghĩa là đối tượng của lớp này có thể được sao chép bằng cách sử dụng phương thức clone(). Khi một lớp triển khai giao diện Cloneable, nó cho phép việc tạo ra một bản sao (shallow copy) của đối tượng.

↳ implements Serializable: Cuối cùng, lớp ArrayDeque triển khai giao diện Serializable, có nghĩa là các đối tượng của lớp này có thể được tuần tự hóa (serialize), tức là chúng có thể được chuyển đổi thành một chuỗi byte để lưu trữ hoặc truyền qua mạng và sau đó có thể được khôi phục (deserialize) thành đối tượng gốc.s

Các constructor của lớp ArrayDeque

Khi làm việc với ArrayDeque, một triển khai linh hoạt của cấu trúc dữ liệu Deque (Double-ended Queue) cho phép bạn thêm và xóa phần tử từ cả hai đầu, việc khởi tạo nó đúng cách là bước đầu tiên quan trọng. ArrayDeque cung cấp các constructor khác nhau để bạn có thể tạo một deque rỗng với dung lượng mặc định, tùy chỉnh dung lượng ban đầu, hoặc khởi tạo nó với các phần tử từ một tập hợp đã có. Dưới đây là các constructor mà bạn có thể sử dụng:

↳ ArrayDeque(): Tạo một deque rỗng với dung lượng ban đầu là 16.

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

↳ ArrayDeque(int numElements): Tạo một deque rỗng với dung lượng ban đầu đủ để chứa số lượng phần tử được chỉ định.

Ví dụ

// Tạo một deque rỗng với dung lượng ban đầu là 16
ArrayDeque<String> deque1 = new ArrayDeque<>();

// Tạo một deque chứa các phần tử từ một List
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
ArrayDeque<Integer> deque2 = new ArrayDeque<>(numbers);

// Tạo một deque rỗng với dung lượng ban đầu đủ để chứa 10 phần tử
ArrayDeque<Character> deque3 = new ArrayDeque<>(10);

Lưu ý:

↳ ArrayDeque là một triển khai của giao diện Deque, cung cấp các phương thức để thao tác với cả đầu và cuối của danh sách.

↳ Dung lượng ban đầu của ArrayDeque sẽ được tăng tự động nếu cần thiết.

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

Lớp ArrayDeque cung cấp các phương thức để thao tác với cả đầu và cuối của một danh sách. Bạn có thể thêm, xóa, lấy và duyệt qua các phần tử trong deque theo thứ tự mong muốn. Dưới đây là danh sách tất cả các phương thức của Class ArrayDequ<E> trong Java:

↳ boolean add(E e): Thêm phần tử e vào cuối deque.

↳ void addFirst(E e): Thêm phần tử e vào đầu deque.

↳ void addLast(E e): Thêm phần tử e vào cuối deque.

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

↳ ArrayDeque<E> clone(): Trả về một bản sao của deque này.

↳ boolean contains(Object o): Trả về true nếu deque chứa phần tử o.

↳ Iterator<E> descendingIterator(): Trả về một iterator duyệt qua các phần tử trong deque theo thứ tự ngược.

↳ E element(): Lấy phần tử đầu tiên của deque mà không xóa nó.

↳ E getFirst(): Lấy phần tử đầu tiên của deque mà không xóa nó.

↳ E getLast(): Lấy phần tử cuối cùng của deque mà không xóa nó.

↳ boolean isEmpty(): Kiểm tra xem deque có rỗng hay không.

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

↳ boolean offer(E e): Thêm phần tử e vào cuối deque.

↳ boolean offerFirst(E e): Thêm phần tử e vào đầu deque.

↳ boolean offerLast(E e): Thêm phần tử e vào cuối deque.

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

↳ E peekFirst(): Lấy phần tử đầu tiên của deque mà không xóa nó.

↳ E peekLast(): Lấy phần tử cuối cùng của deque mà không xóa nó.

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

↳ E pollFirst(): Lấy và xóa phần tử đầu tiên của deque.

↳ E pollLast(): Lấy và xóa phần tử cuối cùng của deque.

↳ E pop(): Lấy và xóa phần tử đầu tiên của deque (được coi là đỉnh của stack).

↳ void push(E e): Thêm phần tử e vào đầu deque (được coi là đáy của stack).

↳ E remove(): Lấy và xóa phần tử đầu tiên của deque.

↳ boolean remove(Object o): Xóa phần tử đầu tiên xuất hiện của phần tử o trong deque.

↳ E removeFirst(): Lấy và xóa phần tử đầu tiên của deque.

↳ boolean removeFirstOccurrence(Object o): Xóa phần tử đầu tiên xuất hiện của phần tử o trong deque (khi duyệt từ đầu đến cuối).

↳ E removeLast(): Lấy và xóa phần tử cuối cùng của deque.

↳ boolean removeLastOccurrence(Object o): Xóa phần tử cuối cùng xuất hiện của phần tử o trong deque (khi duyệt từ đầu đến cuối).

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

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

↳ Object[] toArray(): Trả về một mảng chứa tất cả các phần tử trong deque theo thứ tự đúng (từ phần tử đầu tiên đến phần tử cuối cùng).

↳ <T> T[] toArray(T[] a): Trả về một mảng chứa tất cả các phần tử trong deque theo thứ tự đúng (từ phần tử đầu tiên đến phần tử cuối cùng); kiểu thời gian chạy của mảng được trả về là kiểu của mảng đã cho.

Ví dụ sử dụng các phương thức thêm phần tử

Dưới đây là ví dụ sử dụng các phương thức thêm phần tử vào ArrayDeque trong Java, bao gồm offerFirst(E e), addFirst(E e), offerLast(E e), addLast(E e), và push(E e).

Ví dụ: Example.java

import java.util.ArrayDeque;

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

        // Sử dụng offerFirst() để thêm phần tử vào đầu deque
        deque.offerFirst("Apple");
        System.out.println("Sau khi offerFirst('Apple'): " + deque);

        // Sử dụng addFirst() để thêm phần tử vào đầu deque
        deque.addFirst("Banana");
        System.out.println("Sau khi addFirst('Banana'): " + deque);

        // Sử dụng offerLast() để thêm phần tử vào cuối deque
        deque.offerLast("Cherry");
        System.out.println("Sau khi offerLast('Cherry'): " + deque);

        // Sử dụng addLast() để thêm phần tử vào cuối deque
        deque.addLast("Date");
        System.out.println("Sau khi addLast('Date'): " + deque);

        // Sử dụng push() để thêm phần tử vào đầu deque (tương tự như một stack)
        deque.push("Elderberry");
        System.out.println("Sau khi push('Elderberry'): " + deque);
    }
}

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

Sau khi offerFirst('Apple'): [Apple]
Sau khi addFirst('Banana'): [Banana, Apple]
Sau khi offerLast('Cherry'): [Banana, Apple, Cherry]
Sau khi addLast('Date'): [Banana, Apple, Cherry, Date]
Sau khi push('Elderberry'): [Elderberry, Banana, Apple, Cherry, Date]

Ví dụ sử dụng các phương thức để lấy phần tử

Dưới đây là ví dụ sử dụng các phương thức để lấy phần tử từ ArrayDeque trong Java, bao gồm các phương thức như peek(), peekFirst(), peekLast(), poll(), pollFirst(), pollLast(), element(), getFirst(), getLast(), và pop().

Ví dụ: Example.java

import java.util.ArrayDeque;

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

        // Thêm một số phần tử vào deque
        deque.add("Apple");
        deque.add("Banana");
        deque.add("Cherry");
        deque.add("Date");

        // Sử dụng peek() để lấy phần tử đầu tiên mà không xóa
        System.out.println("peek(): " + deque.peek());

        // Sử dụng peekFirst() để lấy phần tử đầu tiên mà không xóa
        System.out.println("peekFirst(): " + deque.peekFirst());

        // Sử dụng peekLast() để lấy phần tử cuối cùng mà không xóa
        System.out.println("peekLast(): " + deque.peekLast());

        // Sử dụng poll() để lấy và xóa phần tử đầu tiên
        System.out.println("poll(): " + deque.poll());

        // Sử dụng pollFirst() để lấy và xóa phần tử đầu tiên
        System.out.println("pollFirst(): " + deque.pollFirst());

        // Sử dụng pollLast() để lấy và xóa phần tử cuối cùng
        System.out.println("pollLast(): " + deque.pollLast());

        // Sử dụng element() để lấy phần tử đầu tiên mà không xóa (giống peek())
        System.out.println("element(): " + deque.element());

        // Thêm lại một số phần tử để minh họa tiếp
        deque.add("Elderberry");
        deque.add("Fig");

        // Sử dụng getFirst() để lấy phần tử đầu tiên mà không xóa
        System.out.println("getFirst(): " + deque.getFirst());

        // Sử dụng getLast() để lấy phần tử cuối cùng mà không xóa
        System.out.println("getLast(): " + deque.getLast());

        // Sử dụng pop() để lấy và xóa phần tử đầu tiên (được coi như một stack)
        System.out.println("pop(): " + deque.pop());

        // Hiển thị trạng thái của deque sau các thao tác
        System.out.println("Trạng thái cuối cùng của deque: " + deque);
    }
}

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

peek(): Apple
peekFirst(): Apple
peekLast(): Date
poll(): Apple
pollFirst(): Banana
pollLast(): Date
element(): Cherry
getFirst(): Cherry
getLast(): Fig
pop(): Cherry
Trạng thái cuối cùng của deque: [Elderberry, Fig]

Lưu ý về sự khác biệt giữa các phương thức:

↳ offer vs. add: offer thường được ưu tiên hơn add vì nó trả về boolean để chỉ ra thành công hay thất bại, tránh ném ngoại lệ.

↳ First vs. Last: Chỉ định vị trí đầu hoặc cuối của deque.

↳ peek vs. poll: peek chỉ lấy mà không xóa, poll lấy và xóa.

↳ pop và push: Được sử dụng khi deque được xem như một stack.

Ví dụ sử dụng các phương thức để xóa phần tử

Dưới đây là ví dụ sử dụng các phương thức để xóa phần tử từ ArrayDeque trong Java, bao gồm các phương thức như remove(), removeFirst(), removeLast(), remove(Object o), removeFirstOccurrence(Object o), removeLastOccurrence(Object o), và clear().

Ví dụ: Example.java

import java.util.ArrayDeque;

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

        // Thêm một số phần tử vào deque
        deque.add("Apple");
        deque.add("Banana");
        deque.add("Cherry");
        deque.add("Apple");
        deque.add("Date");

        System.out.println("Deque ban đầu: " + deque);

        // Sử dụng remove() để xóa phần tử đầu tiên
        deque.remove();
        System.out.println("Sau remove(): " + deque);

        // Sử dụng removeFirst() để xóa phần tử đầu tiên
        deque.removeFirst();
        System.out.println("Sau removeFirst(): " + deque);

        // Sử dụng removeLast() để xóa phần tử cuối cùng
        deque.removeLast();
        System.out.println("Sau removeLast(): " + deque);

        // Sử dụng remove(Object o) để xóa phần tử đầu tiên có giá trị là "Apple"
        deque.remove("Apple");
        System.out.println("Sau remove('Apple'): " + deque);

        // Thêm lại phần tử để minh họa các phương thức khác
        deque.addFirst("Apple");
        deque.addLast("Banana");

        System.out.println("Deque sau khi thêm lại phần tử: " + deque);

        // Sử dụng removeFirstOccurrence(Object o) để xóa lần xuất hiện đầu tiên của "Banana"
        deque.removeFirstOccurrence("Banana");
        System.out.println("Sau removeFirstOccurrence('Banana'): " + deque);

        // Sử dụng removeLastOccurrence(Object o) để xóa lần xuất hiện cuối cùng của "Apple"
        deque.removeLastOccurrence("Apple");
        System.out.println("Sau removeLastOccurrence('Apple'): " + deque);

        // Sử dụng clear() để xóa tất cả các phần tử
        deque.clear();
        System.out.println("Sau clear(): " + deque);
    }
}

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

Deque ban đầu: [Apple, Banana, Cherry, Apple, Date]
Sau remove(): [Banana, Cherry, Apple, Date]
Sau removeFirst(): [Cherry, Apple, Date]
Sau removeLast(): [Cherry, Apple]
Sau remove('Apple'): [Cherry]
Deque sau khi thêm lại phần tử: [Apple, Cherry, Banana]
Sau removeFirstOccurrence('Banana'): [Apple, Cherry]
Sau removeLastOccurrence('Apple'): [Cherry]
Sau clear(): []

Ví dụ sử dụng các phương thức để kiểm tra phần tử

Dưới đây là ví dụ sử dụng các phương thức để kiểm tra phần tử trong ArrayDeque trong Java, bao gồm các phương thức như contains(Object o), isEmpty(), và size().

Ví dụ: Example.java

import java.util.ArrayDeque;

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

        // Thêm một số phần tử vào deque
        deque.add("Apple");
        deque.add("Banana");
        deque.add("Cherry");

        // Kiểm tra xem deque có chứa phần tử "Banana" không
        boolean containsBanana = deque.contains("Banana");
        System.out.println("Deque có chứa 'Banana' không? " + containsBanana);

        // Kiểm tra xem deque có rỗng không
        boolean isEmpty = deque.isEmpty();
        System.out.println("Deque có rỗng không? " + isEmpty);

        // Lấy số lượng phần tử trong deque
        int size = deque.size();
        System.out.println("Số lượng phần tử trong deque: " + size);
    }
}

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

Deque có chứa 'Banana' không? true
Deque có rỗng không? false
Số lượng phần tử trong deque: 3

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

Dưới đây là ví dụ sử dụng phương thức iterator() của lớp ArrayDeque 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 ArrayDeque.

Ví dụ: Example.java

import java.util.ArrayDeque;
import java.util.Iterator;

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

        // Thêm một số phần tử vào deque
        deque.add("Apple");
        deque.add("Banana");
        deque.add("Cherry");
        deque.add("Date");
        deque.add("Elderberry");

        // Lấy iterator để duyệt qua các phần tử
        Iterator<String> iterator = deque.iterator();

        // Sử dụng iterator để duyệt và in các phần tử
        System.out.println("Duyệt qua các phần tử trong ArrayDeque:");
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }
    }
}

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

Duyệt qua các phần tử trong ArrayDeque:
Apple
Banana
Cherry
Date
Elderberry

Ví dụ sử dụng các phương thức descendingIterator()

Dưới đây là ví dụ sử dụng phương thức descendingIterator() của lớp ArrayDeque trong Java. Phương thức này cho phép bạn duyệt ArrayDeque theo thứ tự ngược lại.

Ví dụ: Example.java

import java.util.ArrayDeque;
import java.util.Iterator;

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

        // Thêm một số phần tử vào deque
        deque.add("Apple");
        deque.add("Banana");
        deque.add("Cherry");
        deque.add("Date");
        deque.add("Elderberry");

        // Lấy descending iterator để duyệt qua các phần tử theo thứ tự ngược lại
        Iterator<String> descendingIterator = deque.descendingIterator();

        // Sử dụng descending iterator để duyệt và in các phần tử
        System.out.println("Duyệt qua các phần tử trong ArrayDeque theo thứ tự ngược lại:");
        while (descendingIterator.hasNext()) {
            String element = descendingIterator.next();
            System.out.println(element);
        }
    }
}

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

Duyệt qua các phần tử trong ArrayDeque theo thứ tự ngược lại:
Elderberry
Date
Cherry
Banana
Apple

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

Dưới đây là một ví dụ về việc sử dụng ArrayDeque với các đối tượng do người dùng định nghĩa trong Java. Trước tiên, chúng ta sẽ tạo một lớp đối tượng tùy chỉnh, ví dụ như Student, với các thuộc tính như id và name.

Ví dụ: Example.java

import java.util.ArrayDeque;
class Student {
    int id;
    String name;

    // Constructor
    public Student(int id, String name) {
        this.id = id;
        this.name = name;
    }

    // Override phương thức toString để hiển thị thông tin của đối tượng Student
    @Override
    public String toString() {
        return "Student{ID=" + id + ", Tên='" + name + "'}";
    }
}

public class Example {
    public static void main(String[] args) {
        // Tạo một ArrayDeque chứa các đối tượng Student
        ArrayDeque<Student> studentDeque = new ArrayDeque<>();

        // Thêm các đối tượng Student vào deque
        studentDeque.addFirst(new Student(1, "Alice"));
        studentDeque.addLast(new Student(2, "Bob"));
        studentDeque.addFirst(new Student(3, "Charlie"));

        // In ra các phần tử trong deque
        System.out.println("Các phần tử trong deque:");
        for (Student student : studentDeque) {
            System.out.println(student);
        }

        // Lấy phần tử đầu tiên mà không xóa nó
        Student firstStudent = studentDeque.peekFirst();
        System.out.println("\nPhần tử đầu tiên (không xóa): " + firstStudent);

        // Lấy và xóa phần tử cuối cùng
        Student lastStudent = studentDeque.pollLast();
        System.out.println("Phần tử cuối cùng (đã xóa): " + lastStudent);

        // In ra các phần tử còn lại trong deque
        System.out.println("\nCác phần tử còn lại trong deque:");
        for (Student student : studentDeque) {
            System.out.println(student);
        }
    }
}

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

Student{ID=3, Tên='Charlie'}
Student{ID=1, Tên='Alice'}
Student{ID=2, Tên='Bob'}

Phần tử đầu tiên (không xóa): Student{ID=3, Tên='Charlie'}
Phần tử cuối cùng (đã xóa): Student{ID=2, Tên='Bob'}

Các phần tử còn lại trong deque:
Student{ID=3, Tên='Charlie'}
Student{ID=1, Tên='Alice'}

So sánh class PriorityQueue và class ArrayDeque

Cả PriorityQueue và ArrayDeque đều là các lớp trong Java thuộc gói java.util, được sử dụng để lưu trữ danh sách các phần tử. Tuy nhiên, chúng có những đặc điểm và hiệu suất khác nhau phù hợp với các trường hợp sử dụng cụ thể. Dưới đây là sự so sánh giữa PriorityQueue và ArrayDeque:

Điểm giống giữa class PriorityQueue và class ArrayDeque

↳ Không đồng bộ hóa: Cả PriorityQueue và ArrayDeque đều không đồng bộ hóa (không synchronized). Điều này có nghĩa là bạn phải quản lý đồng bộ hóa thủ công nếu bạn sử dụng chúng trong môi trường đa luồng.

↳ Tự động mở rộng kích thước: Cả hai lớp đều tự động mở rộng kích thước khi cần thiết. Bạn không cần phải chỉ định kích thước cụ thể khi khởi tạo các đối tượng của chúng; chúng sẽ tự động tăng kích thước khi số lượng phần tử vượt quá dung lượng hiện tại.

↳ Sử dụng trong cấu trúc dữ liệu hàng đợi: PriorityQueue và ArrayDeque đều có thể được sử dụng như một hàng đợi (queue), mặc dù cách chúng tổ chức và xử lý phần tử là khác nhau. PriorityQueue sắp xếp các phần tử theo thứ tự ưu tiên, trong khi ArrayDeque hỗ trợ thêm và lấy phần tử từ cả hai đầu của hàng đợi.

↳ Không cho phép phần tử null: PriorityQueue không cho phép phần tử null. ArrayDeque cũng không cho phép thêm phần tử null nếu được sử dụng theo cách chính thức. Tuy nhiên, ArrayDeque có thể chứa null trong một số tình huống cụ thể tùy thuộc vào cách sử dụng của bạn.

↳ Duyệt qua phần tử: Cả hai lớp đều cung cấp cách để duyệt qua các phần tử. Bạn có thể sử dụng các phương thức như iterator() và spliterator() để truy cập và duyệt qua các phần tử trong cả PriorityQueue và ArrayDeque.

↳ Cả hai lớp đều cung cấp các phương thức để thêm, lấy và xóa phần tử. Ví dụ:

↳ PriorityQueue: add(), offer(), peek(), poll()

↳ ArrayDeque: addFirst(), addLast(), peekFirst(), pollFirst()

Điểm khác biệt giữa class PriorityQueue và class ArrayDeque

Tính năngPriorityQueueArrayDeque
Tính chấtHàng đợi ưu tiên, sắp xếp các phần tử theo thứ tự ưu tiên.Deque (Double-Ended Queue), cho phép thêm và lấy phần tử từ cả hai đầu.
Cấu trúc dữ liệuSử dụng heap (đống) để duy trì thứ tự ưu tiên.Sử dụng mảng vòng để lưu trữ phần tử.
Thứ tự phần tửCác phần tử được sắp xếp theo thứ tự ưu tiên.Phần tử không được sắp xếp, mà được thêm vào hoặc lấy từ đầu hoặc cuối.
Thêm phần tửadd(E e), offer(E e): Thêm phần tử vào hàng đợi.addFirst(E e), addLast(E e), offerFirst(E e), offerLast(E e), push(E e): Thêm phần tử vào đầu hoặc cuối deque.
Lấy phần tửpeek(): Lấy phần tử có độ ưu tiên cao nhất mà không xóa.peek(), peekFirst(), peekLast(): Lấy phần tử đầu hoặc cuối mà không xóa.
Xóa phần tửpoll(): Lấy và xóa phần tử có độ ưu tiên cao nhất.removeFirst(), removeLast(), remove(Object o): Xóa phần tử đầu, cuối hoặc một phần tử cụ thể.
Duyệt qua phần tửSử dụng iterator(): Duyệt qua các phần tử không theo thứ tự ưu tiên.Sử dụng iterator(), descendingIterator(): Duyệt qua phần tử theo thứ tự tăng dần hoặc giảm dần.
Tạo một viewKhông hỗ trợ tạo view của phần tử.descendingIterator(): Trả về một iterator duyệt qua các phần tử theo thứ tự giảm dần.

Khi nào nên sử dụng?

↳ PriorityQueue: Sử dụng khi bạn cần sắp xếp các phần tử theo ưu tiên, tìm phần tử lớn nhất/nhỏ nhất hoặc thực hiện các thuật toán ưu tiên.

↳ ArrayDeque: Sử dụng khi bạn cần một cấu trúc dữ liệu hiệu quả cho các hoạt động thêm/xóa ở đầu và cuối, hoặc khi bạn muốn mô phỏng stack hoặc queue.

Dưới đây là ví dụ đơn giản minh họa sự khác nhau giữa PriorityQueue và ArrayDeque:

Ví dụ: Example.java

import java.util.ArrayDeque;
import java.util.PriorityQueue;

public class Example {
    public static void main(String[] args) {
        // Tạo PriorityQueue để lưu trữ các số nguyên
        PriorityQueue<Integer> pq = new PriorityQueue<>(); 
        // Thêm các phần tử vào PriorityQueue
        pq.add(30);
        pq.add(10);
        pq.add(20);
        pq.add(40);
        
        // Tạo ArrayDeque để lưu trữ các số nguyên
        ArrayDeque<Integer> deque = new ArrayDeque<>();
        
        // Thêm các phần tử vào ArrayDeque
        deque.addLast(30);
        deque.addLast(10);
        deque.addLast(20);
        deque.addLast(40);

        // In các phần tử theo thứ tự ưu tiên
        System.out.println("PriorityQueue:");
        while (!pq.isEmpty()) {
            System.out.println(pq.poll()); // Lấy và xóa phần tử theo độ ưu tiên
        }
        // In các phần tử theo thứ tự thêm vào
        System.out.println("ArrayDeque:");
        while (!deque.isEmpty()) {
            System.out.println(deque.pollFirst()); // Lấy và xóa phần tử từ đầu
        }
    }
}

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

PriorityQueue:
10
20
30
40
ArrayDeque:
30
10
20
40

Như vậy, chúng ta đã tìm hiểu các phương thức chính của ArrayDeque. Với khả năng hoạt động như cả một Stack (LIFO) và một Queue (FIFO), cho phép thêm/xóa phần tử hiệu quả từ cả hai đầu, ArrayDeque là một triển khai cực kỳ linh hoạt và hiệu quả trong Java Collections Framework. Nắm vững các phương thức này sẽ giúp bạn tối ưu hóa việc quản lý dữ liệu trong nhiều tình huống khác nhau, từ xử lý hàng đợi đến quản lý lịch sử thao tác.

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.”