Post

Set

1. Giới thiệu

Set là một interface kế thừa Collection interface trong java. Set trong java là một Collection không thể chứa các phần tử trùng lặp. Set được triển khai bởi Hashset, LinkedHashset, Treeset hoặc EnumSet.

  • HashSet lưu trữ các phần tử của nó trong bảng băm, là cách thực hiện tốt nhất, tuy nhiên nó không đảm bảo về thứ tự các phần tử được chèn vào.
  • TreeSet lưu trữ các phần tử của nó trong một cây, sắp xếp các phần tử của nó dựa trên các giá trị của chúng, về cơ bản là chậm hơn HashSet.
  • LinkedHashSet được triển khai dưới dạng bảng băm với có cấu trúc dữ liệu danh sách liên kết, sắp xếp các phần tử của nó dựa trên thứ tự chúng được chèn vào Set (thứ tự chèn).
  • EnumSet là một cài đặt chuyên biệt để sử dụng với các kiểu enum.

Set Interface trong Java chỉ chứa các phương thức được kế thừa từ Collection và thêm sự giới hạn về việc ngăn cấm các phần tử bản sao.

Set Interface cũng thêm các hoạt động hashCode cho phép Set Interface so sánh một cách có ý nghĩa ngay cả khi kiểu triển khai của nó là khác.

Set

Trong hình trên, ta thấy bộ điều khiển Set được extends từ SortedSet interface. Vì một Set không quan tâm đế thứ tự mà ta chèn vào, nên Set interface cung cấp các triển khai không cần thông qua Set. Lớp thực hiện việc điều hướng là TreeSet – là một cây tự cân bằng. Do đó, interface này cung cấp cho chúng ta các để điều hướng qua Tree. Một Set interface có thể được khai báo như sau:

1
public interface Set extends Collection

Code demo một Set:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.HashSet;
import java.util.Set;

public class SetDemo {
	public static void main(String[] args) {
		
		// Khởi tạo Set sử dụng HashSet
		Set<String> hash_Set = new HashSet<String>();

		// Thêm các phần tử vào Set
		hash_Set.add("Michael");
		hash_Set.add("Corleone");
		hash_Set.add("Sonny");
		hash_Set.add("Corleone");
		hash_Set.add("Vito");

		// In các phần tử trong Set
		System.out.println(hash_Set);
	}
}

Tạo một đối tượng Set

Vì Set là một Interface nên không thể tạo một đối tượng thuộc kiểu Set. Chúng ta luôn cần một lớp mở rộng List để tạo đối tượng. Một Set được gọi là an toàn được khởi tạo như sau:

1
2
// Obj là đối tượng được lưu trữ trong Set
Set<Obj> set = new HashSet<Obj> ();

2. Các thao tác trên Set Interface

Set interface ho phép người dùng thực hiện các phép toán cơ bản trên Set.

Hãy lấy hai mảng để hiểu các hoạt động cơ bản này. Cho set1 = [1, 3, 2, 4, 8, 9, 0]set2 = [1, 3, 7, 5, 4, 0, 7, 5]. Sau đó, các phép toán có thể có trên các Set là:

Intersection (giao): hép toán này trả về tất cả các phần tử chung từ hai Set đã cho. Đối với hai Set trên, intersection sẽ là:

1
Intersection = [0, 1, 3, 4]

Union (hợp): Phép toán trả về tất cả các phần tử có ở hai tập hơp. Đối với hai Set trên, Union sẽ là:

1
Union = [0, 1, 2, 3, 4, 5, 7, 8, 9]

Difference (tạm dịch - hiệu): Thao tác này loại bỏ tất cả các giá trị có trong một Set này khỏi Set khác. Đối với hai Set trên, Difference sẽ là:

1
Difference = [2, 8, 9]

Code minh hoạ cho 3 thao tác trên:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class SetOperations {
	public static void main(String args[]) {
		
		Set<Integer> a = new HashSet<Integer>();
		a.addAll(Arrays.asList(new Integer[] { 1, 3, 2, 4, 8, 9, 0 }));
		Set<Integer> b = new HashSet<Integer>();
		b.addAll(Arrays.asList(new Integer[] { 1, 3, 7, 5, 4, 0, 7, 5 }));

		// Phép hợp - Union
		Set<Integer> union = new HashSet<Integer>(a);
		union.addAll(b);
		System.out.print("Union of the two Set: ");
		System.out.println(union);

		// Phép giao - intersection
		Set<Integer> intersection = new HashSet<Integer>(a);
		intersection.retainAll(b);
		System.out.print("Intersection of the two Set: ");
		System.out.println(intersection);

		// Phép hiệu - difference
		Set<Integer> difference = new HashSet<Integer>(a);
		difference.removeAll(b);
		System.out.print("Difference of the two Set: ");
		System.out.println(difference);
	}
}

3. Một số thao tác trên SortedSet

Vì Set là một interface nên nó chỉ có thể được sử dụng với một lớp thực thi trong interface này. HashSet là một trong những lớp được sử dụng rộng rãi thực hiện Set Interface. Bây giờ, hãy xem cách thực hiện một vài thao tác thường dùng trên HashSet.

Adding Elements: Để thêm một phần tử vào Set, chúng ta có thể sử dụng phương thức add(). Tuy nhiên, thứ tự chèn không được quan tâm trong Set. Đối với mọi phần tử, một hàm băm được tạo và các giá trị được lưu trữ liên quan đến hàm băm đã tạo, các giá trị được so sánh và sắp xếp theo thứ tự tăng dần. Chúng ta cần lưu ý rằng không cho phép các phần tử trùng lặp và bỏ qua tất cả các phần tử trùng lặp. Ngoài ra, các giá trị Null không được Set chấp nhận.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.HashSet;
import java.util.Set;

public class SetAdd {
	public static void main(String[] args) {
		Set<String> hs = new HashSet<String>();

		// Thêm phần tử sử dụng phương thức add()
		hs.add("B");
		hs.add("B");
		hs.add("C");
		hs.add("A");

		System.out.println(hs);
	}
}

Accessing the Elements: Sau khi thêm các phần tử, nếu chúng ta muốn truy cập các phần tử, chúng ta có thể sử dụng các phương thức có sẵn như contains().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import java.util.HashSet;
import java.util.Set;

public class SetAccess {
	public static void main(String[] args) {
		Set<String> hs = new HashSet<String>();

		// Thêm phần tử sử dụng phương thức add()
		hs.add("A");
		hs.add("B");
		hs.add("C");
		hs.add("A");

		System.out.println("Set is " + hs);

		String check = "D";

		// Kiểm tra xem chuỗi bên trên có tồn tại
		// trong SortedSet hay không
		System.out.println("Contains " + check + " " + hs.contains(check));
	}
}

Removing the Values: Các giá trị có thể được loại bỏ khỏi Set bằng phương thức remove().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.util.HashSet;
import java.util.Set;

public class SetRemove {
	public static void main(String[] args) {
		Set<String> hs = new HashSet<String>();

		// Thêm phần tử sử dụng phương thức add()
		hs.add("A");
		hs.add("B");
		hs.add("C");
		hs.add("B");
		hs.add("D");
		hs.add("E");

		System.out.println("Initial HashSet " + hs);

		// Xoá phần tử B
		hs.remove("B");

		System.out.println("After removing element " + hs);
	}
}

Iterating through the Set: Có nhiều cách khác nhau để lặp lại qua Set. Cách nổi tiếng nhất là sử dụng vòng lặp FOR nâng cao.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.HashSet;
import java.util.Set;

public class SetInteration {
	public static void main(String[] args) {
		Set<String> hs = new HashSet<String>();

		// Thêm phần tử sử dụng phương thức add()
		hs.add("A");
		hs.add("B");
		hs.add("C");
		hs.add("B");
		hs.add("D");
		hs.add("E");

		// Lặp qua Set
		for (String value : hs)
			System.out.print(value + ", ");
		System.out.println();
	}
}

4. Các lớp triển khai Set interface trong Java Collections

HashSet: Lớp Hashset được triển khai collection framework là một triển khai cố hữu của cơ cấu dữ liệu bảng băm. Các đối tượng mà chúng ta chèn vào hashset không đảm bảo sẽ được chèn theo cùng một thứ tự. Các đối tượng được chèn dựa trên mã băm của chúng. Lớp này cũng cho phép chèn các phần tử NULL. Hãy xem cách tạo một đối tượng Set bằng cách sử dụng lớp này.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class SetHashSet {
	public static void main(String[] args) {
		Set<String> h = new HashSet<String>();

		// Thêm các phần tử vào HashSet
		// sử dụng phương thức add()
		h.add("USA");
		h.add("Russia");
		h.add("China");

		// Thêm một phần tử
		h.add("Russia");

		// In HashSet
		System.out.println(h);

		// Xoá phần tử của HashSet
		// sử dụng phuowng thức remove()
		h.remove("China");
		System.out.println("Set after removing " + "China:" + h);

		// Lặp lại các phần tử của HashSet
		System.out.println("Iterating over set:");
		Iterator<String> i = h.iterator();
		while (i.hasNext())
			System.out.println(i.next());
	}
}

EnumSet: Lớp EnumSet được triển khai trong collections framework là một trong những phần triển khai chuyên biệt của Set interface để sử dụng với kiểu enumeration. Đây là một cài đặt hiệu suất cao, nhanh hơn nhiều so với HashSet. Tất cả các phần tử trong một Set enum phải đến từ một kiểu enumeration duy nhất được chỉ định khi Set được tạo một cách rõ ràng hoặc ngầm định. Hãy xem cách tạo một đối tượng Set bằng cách sử dụng lớp này.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.*;

enum enumdemo {
	CODE, LEARN, CONTRIBUTE, QUIZ, MCQ
};

public class SetEnumSet {
	public static void main(String[] args) {
		// Tạo Set
		Set<enumdemo> set;

		// Thêm phần tử
		set = EnumSet.of(enumdemo.QUIZ, enumdemo.CONTRIBUTE, enumdemo.LEARN, enumdemo.CODE);

		System.out.println("Set is: " + set);
	}

LinkedHashSet: Lớp LinkedHashSet được triển khai trong collections framework là một phiên bản có thứ tự của HashSet duy trì một doubly-linked List trên tất cả các phần tử. Khi cần đến các thứ tự lặp lại, lớp này được sử dụng. Khi lặp lại qua một HashSet, thứ tự không thể đoán trước được, trong khi LinkedHashSet cho phép chúng ta lặp qua các phần tử theo thứ tự chúng được chèn vào. Hãy xem cách tạo một đối tượng Set bằng cách sử dụng lớp này.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.*;

public class LinkedHashSet {
	public static void main(String[] args) {
		Set<String> lh = new LinkedHashSet<String>();

		// Thêm phần tử vào LinkedHashSet
		// sử dụng add()
		lh.add("USA");
		lh.add("Russia");
		lh.add("China");

		// Thêm một phần tử cho trùng
		lh.add("Russia");

		// In LinkedHashSet
		System.out.println(lh);

		// Xoá phần tử từ LinkedHashSet
		// sử dụng remove()
		lh.remove("China");
		System.out.println("Set after removing " + "China:" + lh);

		// Lặp qua các phần tử của LinkedHashSet
		System.out.println("Iterating over set:");
		Iterator<String> i = lh.iterator();
		while (i.hasNext())
			System.out.println(i.next());
	}
}

TreeSet: Lớp TreeSet được triển khai trong collections framework, việc triển khai SortedSet Interface và SortedSet mở rộng Set Interface. Nó hoạt động như một Set đơn giản với ngoại lệ là nó lưu trữ các phần tử ở định dạng được sắp xếp. TreeSet sử dụng cấu trúc dữ liệu cây để lưu trữ. Các đối tượng được lưu trữ theo thứ tự sắp xếp, tăng dần. Nhưng chúng ta có thể lặp lại theo thứ tự giảm dần bằng phương thức TreeSet.descendingIterator(). Hãy xem cách tạo một đối tượng Set bằng cách sử dụng lớp này.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.Iterator;
import java.util.Set;

public class TreeSet {
	public static void main(String[] args) {
		Set<String> ts = new TreeSet<String>();

		// Thêm phần tử vào TreeSet
		// sử dụng add()
		ts.add("USA");
		ts.add("Russia");
		ts.add("China");

		// Thêm một phần tử nữa
		ts.add("Russia");

		// Displaying the TreeSet
		System.out.println(ts);

		// Xoá phần tử từ TreeSet
		// sử dụng remove()
		ts.remove("China");
		System.out.println("Set after removing " + "China:" + ts);

		// Lặp qua các phần tử của TreeSe
		System.out.println("Iterating over set:");
		Iterator<String> i = ts.iterator();
		while (i.hasNext())
			System.out.println(i.next());
	}
}

5. Một số phương thức khác của Set Interface cần tham khảo

add(element): Phương thức này được sử dụng để thêm một phần tử cụ thể vào Set. Hàm chỉ thêm phần tử nếu phần tử được chỉ định chưa có trong Set khác hàm trả về giá trị False nếu phần tử đã có trong Set.

addAll(collection): Phương thức này được sử dụng để nối tất cả các phần tử từ Set đã đề cập vào Set hiện có. Các yếu tố được thêm vào ngẫu nhiên mà không theo bất kỳ thứ tự cụ thể nào.

clear(): Phương thức này được sử dụng để xóa tất cả các phần tử khỏi Set nhưng không xóa Set - Set vẫn tồn tại.

contains(element): Phương thức này được sử dụng để kiểm tra xem một phần tử cụ thể có trong Set hay không.

containsAll(collection): Phương thức này được sử dụng để kiểm tra xem Set có chứa tất cả các phần tử có trong Set đã cho hay không. Phương thức này trả về true nếu Set chứa tất cả các phần tử và trả về false nếu thiếu bất kỳ phần tử nào.

hashCode(): Phương thức này được sử dụng để lấy giá trị hashCode cho phiên bản của Set. Nó trả về một giá trị số nguyên là giá trị hashCode.

isEmpty(): Phương thức này được sử dụng để kiểm tra xem Set có trống hay không.

iterator(): Phương thức này được sử dụng để trả về trình lặp của Set. Các phần tử từ Set được trả về theo thứ tự ngẫu nhiên.

remove(element): Phương thức này được sử dụng để loại bỏ phần tử đã cho khỏi Set. Phương thức này trả về True nếu phần tử được chỉ định có mặt trong Set, ngược lại nó trả về False.

removeAll(collection): Phương thức này được sử dụng để xóa tất cả các phần tử khỏi Set. Phương thức này trả về true nếu Set có thay đổi sau lệnh này thực thi.

retainAll(collection): Phương thức này được sử dụng để giữ lại tất cả các phần tử từ Set được đề cập trong Set đã cho. Phương thức này trả về true nếu Set có thay đổi sau lệnh này thực thi

size(): Phương pháp này được sử dụng để lấy kích thước của Set. Nó trả về một giá trị số nguyên biểu thị số phần tử.

toArray(): Phương thức này được sử dụng để tạo một mảng gồm các phần tử giống như của Set.

Đến đây, chúng ta có thể nhận thấy rằng Java có các kiểu cấu trúc dữ liệu mang nét đặc trưng riêng của mình và chúng có mối quan hệ mật thiết – tương hỗ lẫn nhau. Do đó trong quá trình tìm hiểu, bạn cần chủ động kết hợp chúng lại khi có thể để nâng cao hiệu quả công việc. Set hay Map là hai khái niệm hoàn toàn mới so với các kiến thức cơ bản trên giáo trình giảng đường, nhưng nó có vai trò cực lỳ quan trọng trong các dự án thực tế của Java. Vì vậy, bắt tay vào tìm hiểu ngay từ sau bài viết này là điều cần thiết và không bao giờ thừa nếu bạn chọn Java.

Bài viết mang tính chất “ghi chú, lưu trữ, chia sẻ và phi lợi nhuận”.
Nếu bạn thấy hữu ích, đừng quên chia sẻ với bạn bè và đồng nghiệp của mình nhé!

Happy coding! 😎 👍🏻 🚀 🔥

This post is licensed under CC BY 4.0 by the author.