Làm thế nào để duy trì một Danh sách duy nhất trong Java?


104

Làm cách nào để tạo danh sách các đối tượng duy nhất / khác biệt (không trùng lặp) trong Java?

Ngay bây giờ tôi đang sử dụng HashMap<String, Integer>để làm điều này vì khóa bị ghi đè và do đó, cuối cùng chúng ta có thể nhận được khóa HashMap.getKeySet()sẽ là duy nhất. Nhưng tôi chắc chắn nên có một cách tốt hơn để làm điều này vì phần giá trị bị lãng phí ở đây.

Câu trả lời:


164

Bạn có thể sử dụng triển khai Set :

Một số thông tin từ JAVADoc:

Một tập hợp không chứa các phần tử trùng lặp . Chính thức hơn, các tập hợp không chứa cặp phần tử e1 và e2 sao cho e1.equals (e2) và nhiều nhất là một phần tử rỗng. Như tên gọi của nó, giao diện này mô hình hóa sự trừu tượng hóa tập hợp toán học.

Lưu ý: Cần phải hết sức thận trọng nếu các đối tượng có thể thay đổi được sử dụng làm phần tử tập hợp. Hành vi của một tập hợp không được chỉ định nếu giá trị của một đối tượng bị thay đổi theo cách ảnh hưởng đến các phép so sánh ngang bằng trong khi đối tượng là một phần tử trong tập hợp. Một trường hợp đặc biệt của điều cấm này là không cho phép một tập hợp chứa chính nó như một phần tử. '

Đây là những cách triển khai:

  • HashSet

    Lớp này cung cấp hiệu suất thời gian không đổi cho các hoạt động cơ bản (thêm, xóa, chứa và kích thước), giả sử hàm băm phân tán các phần tử đúng cách giữa các nhóm. Lặp lại tập hợp này yêu cầu thời gian tỷ lệ với tổng kích thước của cá thể HashSet (số phần tử) cộng với "dung lượng" của cá thể HashMap hỗ trợ (số lượng nhóm). Vì vậy, điều rất quan trọng là không đặt công suất ban đầu quá cao (hoặc hệ số tải quá thấp) nếu hiệu suất lặp là quan trọng.

    Khi lặp lại một HashSetthứ tự của các phần tử được tạo là không xác định.

  • LinkedHashSet

    Bảng băm và triển khai danh sách liên kết của giao diện Đặt, với thứ tự lặp lại có thể dự đoán được. Việc triển khai này khác với HashSet ở chỗ nó duy trì một danh sách được liên kết kép chạy qua tất cả các mục của nó. Danh sách liên kết này xác định thứ tự lặp lại, là thứ tự mà các phần tử được chèn vào tập hợp (thứ tự chèn). Lưu ý rằng thứ tự chèn không bị ảnh hưởng nếu một phần tử được chèn lại vào tập hợp. (Một phần tử e được chèn lại vào một tập hợp s nếu s.add (e) được gọi khi s.contains (e) sẽ trả về true ngay lập tức trước khi gọi.)

    Vì vậy, đầu ra của đoạn mã trên ...

     Set<Integer> linkedHashSet = new LinkedHashSet<>();
     linkedHashSet.add(3);
     linkedHashSet.add(1);
     linkedHashSet.add(2);
    
     for (int i : linkedHashSet) {
         System.out.println(i);
     }

    ... nhất thiết sẽ là

    3
    1
    2
  • TreeSet

    Việc triển khai này đảm bảo chi phí thời gian log (n) cho các hoạt động cơ bản (thêm, bớt và chứa). Theo mặc định, các phần tử được trả về khi lặp lại được sắp xếp theo " thứ tự tự nhiên " của chúng, vì vậy đoạn mã trên ...

     Set<Integer> treeSet = new TreeSet<>();
     treeSet.add(3);
     treeSet.add(1);
     treeSet.add(2);
    
     for (int i : treeSet) {
         System.out.println(i);
     }

    ... sẽ xuất ra cái này:

    1
    2
    3

    (Bạn cũng có thể chuyển một Comparatorthể hiện cho một phương thức TreeSetkhởi tạo, làm cho nó sắp xếp các phần tử theo một thứ tự khác.)

    Lưu ý rằng thứ tự được duy trì bởi một tập hợp (cho dù có cung cấp bộ so sánh rõ ràng hay không) phải nhất quán với bằng nếu nó triển khai chính xác giao diện Đặt. (Xem So sánh hoặc So sánh để biết định nghĩa chính xác về nhất quán với bằng.) Điều này là như vậy vì giao diện Đặt được định nghĩa theo phép toán bằng, nhưng một cá thể TreeSet thực hiện tất cả các so sánh phần tử bằng cách sử dụng phương thức CompareTo (hoặc so sánh) của nó, vì vậy hai các phần tử được coi là bằng nhau theo phương pháp này, theo quan điểm của tập hợp, bằng nhau. Hành vi của một tập hợp được xác định rõ ràng ngay cả khi thứ tự của nó không nhất quán với bằng; nó chỉ không tuân theo hợp đồng chung của giao diện Set.


Bây giờ tôi đang bối rối, tôi sẽ sử dụng cái nào? Tôi chỉ cần duy trì một danh sách các chuỗi duy nhất. Vì vậy, về cơ bản, ngay cả khi một chuỗi hiện có được thêm vào, nó sẽ thực sự được thêm vào.

1
Sự lựa chọn là của bạn ... HashSet là phổ quát và nhanh chóng, TreeSet được ra lệnh, LinkedHashSet giữ trật tự chèn ...
Frank

6
Đây không phải là LIST ... vì vậy, không phải tất cả các phương pháp giao diện LIST đều có sẵn.
marcolopes

2
Một tập hợp không phải là một danh sách, tôi không thể tra cứu các phần tử theo chỉ mục trong một tập hợp trong thời gian O (1) (truy cập ngẫu nhiên).
wilmol

13

Tôi muốn làm rõ một số điều ở đây cho người đăng ban đầu mà những người khác đã ám chỉ đến nhưng chưa thực sự nêu rõ ràng. Khi bạn nói rằng bạn muốn một Danh sách duy nhất, đó chính là định nghĩa của Tập hợp có Thứ tự. Một số điểm khác biệt chính giữa Giao diện Đặt và Giao diện Danh sách là Danh sách cho phép bạn chỉ định chỉ mục chèn. Vì vậy, câu hỏi đặt ra là bạn có thực sự cần Giao diện danh sách (tức là để tương thích với thư viện của bên thứ 3, v.v.) hay bạn có thể thiết kế lại phần mềm của mình để sử dụng giao diện Đặt không? Bạn cũng phải xem xét những gì bạn đang làm với giao diện. Việc tìm các phần tử theo chỉ số của chúng có quan trọng không? Bạn mong đợi có bao nhiêu phần tử trong tập hợp của mình? Nếu bạn sắp có nhiều yếu tố, việc đặt hàng có quan trọng không?

Nếu bạn thực sự cần một Danh sách chỉ có một ràng buộc duy nhất, có lớp Apache Common Utils org.apache.commons.collections.list.SetUniqueList sẽ cung cấp cho bạn giao diện Danh sách và ràng buộc duy nhất. Xin lưu ý bạn, điều này làm hỏng giao diện Danh sách. Tuy nhiên, bạn sẽ nhận được hiệu suất tốt hơn từ điều này nếu bạn cần tìm kiếm trong danh sách theo chỉ mục. Nếu bạn có thể xử lý giao diện Set và bạn có tập dữ liệu nhỏ hơn, thì LinkedHashSet có thể là một cách tốt để thực hiện. Nó chỉ phụ thuộc vào thiết kế và ý định của phần mềm của bạn.

Một lần nữa, có những ưu và nhược điểm nhất định đối với mỗi bộ sưu tập. Một số chèn nhanh nhưng đọc chậm, một số có đọc nhanh nhưng chèn chậm, v.v. Bạn nên dành một lượng thời gian hợp lý với tài liệu bộ sưu tập để tìm hiểu đầy đủ về các chi tiết tốt hơn của từng lớp và giao diện.


3
Điều này không cung cấp câu trả lời cho câu hỏi. Để phê bình hoặc yêu cầu làm rõ từ tác giả, hãy để lại nhận xét bên dưới bài đăng của họ - bạn luôn có thể nhận xét về bài đăng của chính mình và khi bạn có đủ uy tín, bạn sẽ có thể nhận xét về bất kỳ bài đăng nào .
Zach Saucier

1
Nó thực sự cung cấp một câu trả lời. Nếu anh ấy chỉ muốn một danh sách hoạt động giống như một Set, hãy sử dụng org.apache.commons.collections.list.SetUniqueList, nhưng là một lập trình viên, anh ấy / chúng ta nên cẩn thận hơn điều đó và nên suy nghĩ nhiều hơn về vấn đề. Nếu điều này làm cho câu trả lời của tôi tốt hơn, "Làm thế nào để tạo Danh sách duy nhất trong Java?" Liệt kê uniqueList = new SetUniqueList () ;, đó là cách ....
Paul Connolly

3
Và Zach, tôi không cố gắng để trở thành một kẻ ngốc, nhưng bạn thậm chí đã đọc câu trả lời của tôi trước khi nhận xét của bạn? Hay bạn chỉ không hiểu nó? Nếu bạn không hiểu nó, không sao cả - hãy cho tôi biết và tôi sẽ mở rộng chủ đề. Tôi không nghĩ mình phải viết chuyên luận về cấu trúc dữ liệu để đưa ra câu trả lời thân thiện cho câu hỏi của ai đó. Tôi cũng không quan tâm đến một số cách nhẹ nhàng để xây dựng danh tiếng bình luận của mình khi tôi biết câu trả lời và không ai khác thực sự cung cấp câu trả lời đó.
Paul Connolly

1
Và nhân tiện, tôi không chỉ trích hay yêu cầu tác giả làm rõ, tôi chỉ nói rằng anh ta có thể A) nhanh chóng sử dụng lớp tôi đã cho anh ta, hoặc B) dành thời gian để thực sự hiểu sự khác biệt giữa các lớp này và liên hệ chúng theo nhu cầu của mình. B rõ ràng là mất nhiều thời gian hơn, nhưng sẽ dẫn đến mã tốt hơn trong thời gian dài.
Paul Connolly

8

Sử dụng new HashSet<String> một ví dụ:

import java.util.HashSet;
import java.util.Set;

public class MainClass {
  public static void main(String args[]) {
    String[] name1 = { "Amy", "Jose", "Jeremy", "Alice", "Patrick" };

    String[] name2 = { "Alan", "Amy", "Jeremy", "Helen", "Alexi" };

    String[] name3 = { "Adel", "Aaron", "Amy", "James", "Alice" };

    Set<String> letter = new HashSet<String>();

    for (int i = 0; i < name1.length; i++)
      letter.add(name1[i]);

    for (int j = 0; j < name2.length; j++)
      letter.add(name2[j]);

    for (int k = 0; k < name3.length; k++)
      letter.add(name3[k]);

    System.out.println(letter.size() + " letters must be sent to: " + letter);

  }
}

2
Chỉ cần thêm vào từ chương trình ở trên -> 11 bức thư phải được gửi đến: [Aaron, Alice, James, Adel, Jose, Jeremy, Amy, Alan, Patrick, Helen, Alexi]
Ammad

4

Bạn chỉ có thể sử dụng a HashSet<String>để duy trì một bộ sưu tập các đối tượng duy nhất. Nếu các Integergiá trị trong bản đồ của bạn là quan trọng, thì thay vào đó, bạn có thể sử dụng containsKeyphương pháp bản đồ để kiểm tra xem khóa của bạn đã có trong bản đồ hay chưa.


3

HashSet<String>(hoặc) bất kỳ Settriển khai nào có thể thực hiện công việc cho bạn. Setkhông cho phép trùng lặp.

Đây là javadoc cho HashSet.


2

Tôi không biết điều này hiệu quả như thế nào, Tuy nhiên đã làm việc cho tôi trong một bối cảnh đơn giản.

List<int> uniqueNumbers = new ArrayList<>();

   public void AddNumberToList(int num)
    {
        if(!uniqueNumbers .contains(num)) {
            uniqueNumbers .add(num);
        }
    }

1

Bạn có thể muốn sử dụng một trong các lớp triển khai của java.util.Set<E>Giao diện, ví dụ java.util.HashSet<String> lớp tập hợp.

Một tập hợp không chứa các phần tử trùng lặp. Chính thức hơn, các tập hợp không chứa cặp phần tử e1 và e2 sao cho e1.equals (e2) và nhiều nhất là một phần tử rỗng. Như tên gọi của nó, giao diện này mô hình hóa sự trừu tượng hóa tập hợp toán học.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.