Sự khác nhau giữa map và hashmap

HashMap là một cấu trúc dữ liệu rất mạnh mẽ trong Java. Chúng ta sử dụng nó hàng ngày và gần như trong tất cả các ứng dụng. HashMap là một lớp không đồng bộ trong Java collection.

Vậy bạn đã từng tự hỏi những câu sau đây chưa?

  • Sự khác biệt giữa ConcurrentHashMap và Collections.synchronizedMap(Map) là gì?
  • Sự khác biệt giữa ConcurrentHashMap và Collections.synchronizedMap(Map) về hiệu suất như thế nào?
  • ConcurrentHashMap vs Collections.synchronizedMap()?

Trong bài viết này, chúng ta sẽ lần lượt làm rõ tại sao và làm thế nào để có thể đồng bộ hóa HashMap.

1. Tại sao?

Map object là một container chứa các phần tử, các phần tử đó được tạo thành bởi sự kết hợp của khóa xác định duy nhất và giá trị được ánh xạ. Nếu bạn có ứng dụng mang tính đồng thời cao, bạn có thể muốn sửa đổi hoặc đọc giá trị khóa trong các luồng(threads) khác nhau thì đó là lúc lý tưởng để sử dụng Concurrent Hashmap. Ví dụ như bạn muốn cache dữ liệu, nhiều luồng(threads) có thể đọc dữ liệu cache đồng thời... Vậy thì thread-safe Map có nghĩa là gì? Nếu nhiều luồng(threads) có thể truy cập một HashMap đồng thời, và ít nhất 1 luồng sửa đổi cấu trúc của Map thì nó phải đồng bộ bên ngoài để tránh việc dữ liệu không nhất quán.

2. Làm thế nào?

Có 2 cách để chúng ta có thể đồng bộ hóa một HashMap

  • Java Collections synchronizedMap()
  • Sử dụng ConcurrentHashMap
//Hashtable Map normalMap = new Hashtable(); //synchronizedMap synchronizedHashMap = Collections.synchronizedMap(new HashMap()); //ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap();

3. ConcurrentHashMap

  • Bạn nên sử dụng ConcurrentHashMap khi bạn cần tính đồng bộ trong dự án của bạn.
  • Thread safe mà không cần đồng bộ hóa toàn bộ map.
  • Đọc có thể xảy ra rất nhanh trong khi ghi được thực hiện với một khóa.
  • Không có khóa ở cấp độ object.
  • ConcurrentHashMap không ném ra một ConcurrentModificationException nếu một luồng cố gắng sửa đổi nó trong khi một luồng khác đang lặp qua nó.
  • ConcurrentHashMap sử dụng vô số khóa.

4. SynchronizedHashMap

  • Đồng bộ hóa ở mức độ Object.
  • Mọi hoạt động đọc / ghi cần phải có khóa.
  • Khóa toàn bộ thực thể collection là vấn đề đau đầu về hiệu suất.
  • Về cơ bản, điều này cho phép chỉ một luồng truy cập toàn bộ map và chặn tất cả các luồng khác.
  • Nó có thể gây ra việc tranh chấp tài nguyên.
  • SynchronizedHashMap trả về Iterator, nó có thể thất bại trong việc sửa đổi đồng thời.

5. Ví dụ

  • Tạo class ConcurrentHashMapVsSynchronizedHashMap.java
  • Tạo object HashTable, SynchronizedMap và ConcurrentHashMap
  • Thêm mới và truy xuất 1 triệu entries với Map
  • Đo lường thời gian bắt đầu và kết thúc => milliseconds
  • Sử dụng ExecutorService để chạy 10 luồng(threads) song song
package sync.map.test; import java.util.Collections; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class ConcurrentHashMapVsSynchronizedMap { public final static int THREAD_POOL_SIZE = 10; public static Map hashTable = null; public static Map synchronizedMap = null; public static Map concurrentHashMap = null; public static void main(String[] args) throws InterruptedException { // Test with Hashtable Object hashTable = new Hashtable(); performTest(hashTable); // Test with synchronizedMap Object synchronizedMap = Collections.synchronizedMap(new HashMap()); performTest(synchronizedMap); // Test with ConcurrentHashMap Object concurrentHashMap = new ConcurrentHashMap(); performTest(concurrentHashMap); } public static void performTest(final Map testMap) throws InterruptedException { System.out.println("Test started for: " + testMap.getClass()); long averageTime = 0; for (int i = 0; i < 10; i++) { long startTime = System.nanoTime(); ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE); for (int j = 0; j < THREAD_POOL_SIZE; j++) { executorService.execute(new Runnable() { @SuppressWarnings("unused") @Override public void run() { for (int i = 0; i < 1000000; i++) { Integer randomNumber = (int) Math.ceil(Math.random() * 100000); // Retrieve value. Only for test Integer value = testMap.get(String.valueOf(randomNumber)); // Put value testMap.put(String.valueOf(randomNumber), randomNumber); } } }); } executorService.shutdown(); executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); long endTime = System.nanoTime(); long totalTime = (endTime - startTime) / 1000000L; averageTime += totalTime; System.out.println("1M entried added/retrieved in " + totalTime + " ms"); } System.out.println("For " + testMap.getClass() + " the average time is " + averageTime / 10 + " ms\n"); } }

Đây là kết quả trung bình(10 lần) sau khi đọc ghi 1 triệu entries đối với từng loại Map

Test started for: class java.util.Hashtable

1M entried added/retrieved in 4765 ms

1M entried added/retrieved in 4463 ms

1M entried added/retrieved in 4420 ms

1M entried added/retrieved in 4422 ms

1M entried added/retrieved in 4369 ms

1M entried added/retrieved in 4249 ms

1M entried added/retrieved in 4446 ms

1M entried added/retrieved in 4443 ms

1M entried added/retrieved in 4462 ms

1M entried added/retrieved in 4319 ms

For class java.util.Hashtable the average time is 4435 ms

Test started for: class java.util.Collections$SynchronizedMap

1M entried added/retrieved in 4672 ms

1M entried added/retrieved in 4547 ms

1M entried added/retrieved in 4541 ms

1M entried added/retrieved in 4561 ms

1M entried added/retrieved in 4338 ms

1M entried added/retrieved in 4536 ms

1M entried added/retrieved in 4543 ms

1M entried added/retrieved in 4485 ms

1M entried added/retrieved in 4562 ms

1M entried added/retrieved in 4533 ms

For class java.util.Collections$SynchronizedMap the average time is 4531 ms

Test started for: class java.util.concurrent.ConcurrentHashMap

1M entried added/retrieved in 1936 ms

1M entried added/retrieved in 1369 ms

1M entried added/retrieved in 1341 ms

1M entried added/retrieved in 1337 ms

1M entried added/retrieved in 1359 ms

1M entried added/retrieved in 1359 ms

1M entried added/retrieved in 1338 ms

1M entried added/retrieved in 1328 ms

1M entried added/retrieved in 1350 ms

1M entried added/retrieved in 1344 ms

For class java.util.concurrent.ConcurrentHashMap the average time is 1406 ms

Nội dung bài viết

Chào các em, trong những bài trước anh đã giới thiệu về các tập hợp trong lập trình Java bao gồm List , Vector, Set, Queue, Deque, Map.


Bài viết hôm nay anh sẽ tổng hợp lại các tập hợp trong bộ Java Collection mà chúng ta thường xuyên sử dụng trong lập trình Java. Phân biệt sự khác nhau giữa các tập hợp và giúp các em nhận biết được khi nào thì nên sử dụng chúng. Anh cũng sẽ trình bày về phần performance (hiệu năng) cũng như từng loại tập hợp giúp các em có thể tìm ra được tập hợp tốt nhất để áp dụng vào cho bài toán của mình trong quá trình code các chương trình lập trình hướng đối tượng Java.

1. Các collection trong lập trình java

Sự khác nhau giữa map và hashmap

2. List

Là một cấu trúc dữ liệu có thứ tự (đôi khi còn được gọi là một chuỗi). List có thể chứa các phần tử trùng lặp. Thường có quyền kiểm soát chính xác vị trí các phần tử được chèn vào và có thể truy cập chúng bằng chỉ số (vị trí của chúng).

1 2 3 4 List<String> list = new ArrayList<String>(); list.add("Le Vu Nguyen "); list.add("Java "); list.add("Collection List ");

3. Vector

Là một cấu trúc dữ liệu có thể chứa các phần tử trùng lặp. Duy trì thứ tự của phần tử được thêm vào. Vector là synchronized.

1 2 3 Vector<String> list3 = new Vector<String>(); list3.add("Le Vu Nguyen "); list3.add("Collection");

4. Set

Là một cấu trúc dữ liệu không thể chứa 2 giá trị trùng lặp.

1 2 3 4 Set<Integer> hashsetInteger = new HashSet<>(); hashsetInteger.add(1); hashsetInteger.add(2); hashsetInteger.add(3);

5. Queue (hàng đợi)

Là một cấu trúc dữ liệu được sử dụng để chứa nhiều phần tử trước khi xử lý. Bên cạnh các thao tác cơ bản của collection, Là tập hợp cho phép các phần tử trùng lặp, Không cho phép phần tử null Queue cung cấp các thao tác bổ sung như chèn, lấy ra và kiểm tra. Queue có thể được sử dụng như là FIFO (first-in, first-out - vào trước, ra trước).

1 2 3 4 Queue<String> names = new LinkedList<String>(); names.add("Le Vu Nguyen"); names.add("Qu");

6. Deque

Là một cấu trúc dữ liệu cung cấp các thao tác bổ sung như chèn, lấy ra và kiểm tra. Deque có thể được sử dụng như là FIFO (first-in, first-out - vào trước, ra trước) và LIFO (last-in, first-out - vào sau, ra trước). Trong một Deque, tất cả các phần tử mới có thể được chèn vào, lấy ra và lấy ra ở cả hai đầu.

1 2 3 4 5 6 7 Deque<String> deque = new LinkedList<String>(); deque.add("Nguyên "); // add vào đuôi deque.addFirst("Le Vu "); deque.addLast("Deque"); deque.push("Name (Head)"); //add vào đầu deque.offer("Age 5 (Tail)");

7. Map

Là một đối tượng ánh xạ mỗi key tương ứng với một giá trị. Map không thể chứa giá trị trùng lặp. Mỗi key có thể ánh xạ đến nhiều nhất một giá trị.

1 2 3 4 5 6 7 Map<Integer, String> hashMap = new HashMap<>(); hashMap.put(1, "One"); hashMap.put(0, "Zero"); hashMap.put(2, "Two"); hashMap.put(4, "Four"); hashMap.put(21, "Twenty first"); hashMap.put(5, "Five");

8. Phân biệt Collection vs Collections

  • Collections trong java là kiến trúc để lưu trữ và thao tác với nhóm các đối tượng. Tất cả các hoạt động mà bạn thực hiện trên một dữ liệu như tìm kiếm, phân loại, chèn, xóa,… có thể được thực hiện bởi Java Collections.

  • Java Collection cung cấp nhiều interface (Set, List, Queue, Deque vv) và các lớp (ArrayList, Vector, LinkedList, PriorityQueue, HashSet, LinkedHashSet, TreeSet vv).

9. Sự khác nhau Array và ArrayList

  1. Array
    • là fix size, cố định số lượng phần tử trong mảng
    • Có thể lưu trữ dữ liệu kiểu nguyên thủy và đối tượng.
    • Tốc độ lưu trữ và thao tác nhanh hơn.
    • Chỉ có thuộc tính length
  2. ArrayList
    • Số lượng phần tử co giãn được
    • Chỉ có thể lưu trữ dữ liệu kiểu đối tượng. Kể từ Java 5, kiểu nguyên thủy được tự động chuyển đổi trong các đối tượng được gọi là auto-boxing.
    • Tốc độ lưu trữ vào thao tác chậm hơn.
    • Có nhiều phương thức để thao tác với dữ liệu.

10. Sự khác nhau Set và List

  • List các phần tử có thể trùng lặp
  • Set các phần tử không trùng lặp

11. Sự khác nhau ArrayList và Vector

  1. ArrayList
    • ArrayList là KHÔNG synchronized
    • ArrayList tăng kích thước của nó bằng 50% kích thước mảng.
  2. Vector
    • Vector là synchronized.
    • Vector tăng kích thước của nó bằng cách nhân đôi kích thước mảng.
  1. ArrayList
    • ArrayList sử dụng một mảng động
    • ArrayList nhanh hơn trong việc trữ và lấy dữ liệu
  2. LinkedList
    • LinkedList sử dụng danh sách liên kết doubly
    • LinkedList nhanh hơn trong việc thao tác dữ liệu

13. Sự khác nhau HashSet và TreeSet

HashSet không duy trì thứ tự nào, trong khi TreeSet duy trì thứ tự tăng dần

14. Sự khác biệt giữa HashSet và HashMap

  • HashSet với cấu trúc chứa đựng chỉ là các giá trị.
  • HashMap cấu trúc chứa đựng là key và value

15. Sự khác biệt giữa HashMap và TreeMap

HashMap duy trì không có thứ tự, trong khi TreeMap duy trì thứ tự tăng dần.

16. Sự khác biệt giữa Sự khác nhau giữa HashMap và Hashtable

  1. HashMap
    • HashMap là KHÔNG synchronized
    • HashMap cấu trúc chỉ chứa một key null và nhiều giá trị null.
  2. Hashtable
    • Hashtable là synchronized.
    • Hashtable cấu trúc không chứa bất kỳ key null hoặc giá trị null.

17. Sự khác nhau Set và Map

Set chỉ chứa giá trị, trong khi Map chứa cặp key và value.

18. Video Demo

Sự khác nhau giữa map và hashmap

19. Source code

Sự khác nhau giữa map và hashmap

Mọi người hãy Subscribe kênh youtube dưới đây nhé để cập nhật các video mới nhất về kỹ thuật và kỹ năng mềm