Java: Phát hiện bản sao trong ArrayList?


104

Làm cách nào tôi có thể phát hiện (trả về true / false) liệu ArrayList có chứa nhiều hơn một phần tử trong cùng một phần tử trong Java hay không?

Cảm ơn nhiều, Terry

Chỉnh sửa Quên đề cập rằng tôi không muốn so sánh các "Khối" với nhau mà là các giá trị nguyên của chúng. Mỗi "khối" có một int và đây là điều làm cho chúng khác nhau. Tôi tìm int của một Khối cụ thể bằng cách gọi một phương thức có tên "getNum" (ví dụ: table1 [0] [2] .getNum ();


Nếu "Block" được so sánh bởi một int, bạn có thể phải có Mã băm trả về cùng int đó và có bằng so sánh các int đó.
Paul Tomblin

sử dụng Set thay vì List
dmarquina

Câu trả lời:


192

Đơn giản nhất: kết xuất toàn bộ tập hợp vào một Tập hợp (sử dụng phương thức khởi tạo Set (Collection) hoặc Set.addAll), sau đó xem liệu Tập hợp có cùng kích thước với ArrayList hay không.

List<Integer> list = ...;
Set<Integer> set = new HashSet<Integer>(list);

if(set.size() < list.size()){
    /* There are duplicates */
}

Cập nhật: Nếu tôi hiểu câu hỏi của bạn một cách chính xác, bạn có một mảng Khối 2d, như trong

Bảng khối [] [];

và bạn muốn phát hiện xem có hàng nào trùng lặp không?

Trong trường hợp đó, tôi có thể làm như sau, giả sử rằng Block triển khai "bằng" và "mã băm" một cách chính xác:

for (Block[] row : table) {
   Set set = new HashSet<Block>(); 
   for (Block cell : row) {
      set.add(cell);
   }
   if (set.size() < 6) { //has duplicate
   }
}

Tôi không chắc chắn 100% về điều đó về cú pháp, vì vậy có thể sẽ an toàn hơn nếu viết nó là

for (int i = 0; i < 6; i++) {
   Set set = new HashSet<Block>(); 
   for (int j = 0; j < 6; j++)
    set.add(table[i][j]);
 ...

Set.addtrả về một boolean false nếu mục đang được thêm vào đã có trong tập hợp, vì vậy bạn thậm chí có thể đoản mạch và bỏ đi trên bất kỳ phần bổ sung nào trả về falsenếu tất cả những gì bạn muốn biết là có bất kỳ bản sao nào không.


13
Đảm bảo cũng triển khai hashCode / equals.
jon077

1
Hoặc thậm chí dễ dàng hơn một chút: bọc nó khi tạo tập hợp, ví dụ: HashSet mới (danh sách), thay vì sử dụng addAll.
Fabianamondsg

2
@ jon077: Điều đó phụ thuộc vào định nghĩa của bạn về "trùng lặp".
Michael Myers

Quá trình phát hiện các phần tử trong mảng 2D có giống nhau không? Ví dụ: kiểm tra từ mảng [0] [0] đến mảng [0] [6] (một 'hàng') ..? Cảm ơn nhiều, Terry

Mỗi đối tượng trong mảng giữ một giá trị nguyên. Bằng cách "nhân bản", đối tượng sẽ có cùng giá trị số nguyên.

60

Cải tiến mã, sử dụng giá trị trả về Set#addthay vì so sánh kích thước của danh sách và tập hợp.

public static <T> boolean hasDuplicate(Iterable<T> all) {
    Set<T> set = new HashSet<T>();
    // Set#add returns false if the set does not change, which
    // indicates that a duplicate element has been added.
    for (T each: all) if (!set.add(each)) return true;
    return false;
}

7
Sẽ hiệu quả hơn nếu cho HashSet biết cần phân bổ bao nhiêu không gian Set<T> set = new HashSet<T>(list.size());:? Đưa ra một tham số Danh sách, tôi nghĩ sẽ hiệu quả hơn nếu danh sách không chứa trùng lặp là điều bình thường.
Paul Jackson

1
@PaulJackson Định cỡ dựa trên danh sách đầy đủ có thể sẽ có lợi. Tuy nhiên, nếu trường hợp phổ biến là nó tìm thấy bản sao sớm thì không gian đã bị lãng phí. Ngoài ra, ngay cả việc định kích thước theo HashSetkích thước của danh sách sẽ dẫn đến việc thay đổi kích thước khi chạy qua toàn bộ danh sách vì hệ số tải cơ bản của cấu trúc băm.
Jay Anderson

1
Trừ khi bạn gặp phải các vấn đề thực tế về thời gian chạy hoặc không gian, tôi sẽ không chỉnh sửa mã của bạn như vậy. Tốt nhất nên tránh tối ưu hóa sớm.
akuhn

15

Nếu bạn đang tìm cách tránh có các bản sao, thì bạn chỉ nên cắt bỏ quá trình phát hiện bản sao ở giữa và sử dụng một Bộ .


1
Đảm bảo triển khai hashCode / equals :)
jon077 18/02/09

@ jon077: Không nhất thiết, như tôi vừa nói.
Michael Myers

1
Tuy nhiên sử dụng một Bộ không phát hiện các bản sao. Nó chỉ ngăn cản họ. Tất nhiên, trừ khi bạn kiểm tra kết quả của phương thức thêm như đã lưu ý bởi @akuhn ở trên.
mcallahan

13

Cải tiến mã để trả về các phần tử trùng lặp

  • Có thể tìm thấy các bản sao trong Bộ sưu tập
  • trả lại tập hợp các bản sao
  • Các yếu tố duy nhất có thể nhận được từ Bộ

public static <T> List getDuplicate(Collection<T> list) {

    final List<T> duplicatedObjects = new ArrayList<T>();
    Set<T> set = new HashSet<T>() {
    @Override
    public boolean add(T e) {
        if (contains(e)) {
            duplicatedObjects.add(e);
        }
        return super.add(e);
    }
    };
   for (T t : list) {
        set.add(t);
    }
    return duplicatedObjects;
}


public static <T> boolean hasDuplicate(Collection<T> list) {
    if (getDuplicate(list).isEmpty())
        return false;
    return true;
}

Điều đó thật tuyệt. bạn có một số mã không hợp lệ và có thể đó không phải là cách tối ưu nhất, nhưng cách tiếp cận của bạn hoàn toàn phù hợp! (và nó hoạt động tuyệt vời)
Jules Colle

9

Nếu các phần tử của bạn bằng cách nào đó có thể So sánh được (thực tế là thứ tự có bất kỳ ý nghĩa thực sự nào là không quan trọng - nó chỉ cần phù hợp với định nghĩa của bạn về sự bình đẳng), giải pháp loại bỏ trùng lặp nhanh nhất là sắp xếp danh sách (0 (n log ( n))) sau đó thực hiện một lần vượt qua và tìm kiếm các phần tử lặp lại (nghĩa là các phần tử bằng nhau theo sau nhau) (đây là O (n)).

Độ phức tạp tổng thể sẽ là O (n log (n)), gần giống với những gì bạn sẽ nhận được với một Tập hợp (dài n lần (n)), nhưng với một hằng số nhỏ hơn nhiều. Điều này là do hằng số trong sắp xếp / giảm thiểu kết quả từ chi phí so sánh các phần tử, trong khi chi phí từ tập hợp có nhiều khả năng là kết quả từ tính toán băm, cộng với một (có thể là một vài) so sánh băm. Nếu bạn đang sử dụng triển khai Bộ dựa trên băm, nghĩa là vì dựa trên Cây sẽ cung cấp cho bạn điểm O (n log² (n)), điều này thậm chí còn tệ hơn.

Tuy nhiên, theo tôi hiểu, bạn không cần phải xóa các bản sao mà chỉ cần kiểm tra sự tồn tại của chúng. Vì vậy, bạn nên viết mã thủ công một thuật toán sắp xếp hợp nhất hoặc đống trên mảng của bạn, điều này chỉ đơn giản là thoát trả về true (tức là "có sự trùng lặp") nếu bộ so sánh của bạn trả về 0 và nếu không thì hoàn thành việc sắp xếp và duyệt qua kiểm tra mảng đã sắp xếp để tìm số lần lặp lại . Trong một sắp xếp hợp nhất hoặc sắp xếp theo đống, thực sự, khi sắp xếp hoàn tất, bạn sẽ so sánh mọi cặp trùng lặp trừ khi cả hai phần tử đã ở vị trí cuối cùng của chúng (điều này khó xảy ra). Do đó, một thuật toán sắp xếp được tinh chỉnh sẽ mang lại một sự cải thiện hiệu suất rất lớn (Tôi sẽ phải chứng minh điều đó, nhưng tôi đoán thuật toán được tinh chỉnh phải ở dạng O (log (n)) trên dữ liệu ngẫu nhiên đồng nhất)


Trong trường hợp này, n là 6 vì vậy tôi sẽ không lãng phí nhiều thời gian vào các chi tiết triển khai, nhưng tôi sẽ giữ ý tưởng của bạn về loại đống đặc biệt nếu tôi cần làm điều gì đó như vậy.
Paul Tomblin

Tôi không hiểu đoạn thứ ba. Mergesort và heapsort đều là O (nlog (n)), không phải O (log (n)) như bạn viết; ngay cả khi bạn thoát ra sau khi xác định được bản sao, điều đó vẫn không thay đổi độ phức tạp về thời gian của bạn ...
ChaimKut

8

Tôi cần thực hiện một hoạt động tương tự cho một Stream, nhưng không thể tìm thấy một ví dụ tốt. Đây là những gì tôi nghĩ ra.

public static <T> boolean areUnique(final Stream<T> stream) {
    final Set<T> seen = new HashSet<>();
    return stream.allMatch(seen::add);
}

Điều này có lợi thế là đoản mạch khi các bản sao được tìm thấy sớm thay vì phải xử lý toàn bộ luồng và không phức tạp hơn nhiều so với việc chỉ đặt mọi thứ vào a Setvà kiểm tra kích thước. Vì vậy, trường hợp này đại khái sẽ là:

List<T> list = ...
boolean allDistinct = areUnique(list.stream());

7

Với Java 8+, bạn có thể sử dụng Stream API:

boolean areAllDistinct(List<Block> blocksList) {
    return blocksList.stream().map(Block::getNum).distinct().count() == blockList.size();
}

2

Nói một cách đơn giản: 1) đảm bảo tất cả các mục đều có thể so sánh được 2) sắp xếp mảng 2) lặp qua mảng và tìm các bản sao


1

Để biết các bản sao trong một danh sách, hãy sử dụng mã sau: Nó sẽ cung cấp cho bạn tập hợp chứa các bản sao.

 public Set<?> findDuplicatesInList(List<?> beanList) {
    System.out.println("findDuplicatesInList::"+beanList);
    Set<Object> duplicateRowSet=null;
    duplicateRowSet=new LinkedHashSet<Object>();
            for(int i=0;i<beanList.size();i++){
                Object superString=beanList.get(i);
                System.out.println("findDuplicatesInList::superString::"+superString);
                for(int j=0;j<beanList.size();j++){
                    if(i!=j){
                         Object subString=beanList.get(j);
                         System.out.println("findDuplicatesInList::subString::"+subString);
                         if(superString.equals(subString)){
                             duplicateRowSet.add(beanList.get(j));
                         }
                    }
                }
            }
            System.out.println("findDuplicatesInList::duplicationSet::"+duplicateRowSet);
        return duplicateRowSet;
  }

1

cách tốt nhất để xử lý vấn đề này là sử dụng HashSet :

ArrayList<String> listGroupCode = new ArrayList<>();
listGroupCode.add("A");
listGroupCode.add("A");
listGroupCode.add("B");
listGroupCode.add("C");
HashSet<String> set = new HashSet<>(listGroupCode);
ArrayList<String> result = new ArrayList<>(set);

Chỉ cần in kết quả ArrayList và xem kết quả mà không cần bản sao :)


1

Nếu bạn muốn tập hợp các giá trị trùng lặp:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class FindDuplicateInArrayList {

    public static void main(String[] args) {

        Set<String> uniqueSet = new HashSet<String>();
        List<String> dupesList = new ArrayList<String>();
        for (String a : args) {
            if (uniqueSet.contains(a))
                dupesList.add(a);
            else
                uniqueSet.add(a);
        }
        System.out.println(uniqueSet.size() + " distinct words: " + uniqueSet);
        System.out.println(dupesList.size() + " dupesList words: " + dupesList);
    }
}

Và có lẽ cũng nên nghĩ đến việc cắt bớt các giá trị hoặc sử dụng chữ thường ... tùy trường hợp của bạn.


Câu trả lời đơn giản nhất và tốt nhất nếu bạn muốn các bản sao, để đạt hiệu suất, bạn có thể bắt đầu gợi ý UniqueSet với kích thước args.
Christophe Roussy

0
    String tempVal = null;
    for (int i = 0; i < l.size(); i++) {
        tempVal = l.get(i); //take the ith object out of list
        while (l.contains(tempVal)) {
            l.remove(tempVal); //remove all matching entries
        }
        l.add(tempVal); //at last add one entry
    }

Lưu ý: điều này sẽ có tác động lớn đến hiệu suất mặc dù các mục bị xóa khỏi đầu danh sách. Để giải quyết vấn đề này, chúng tôi có hai lựa chọn. 1) lặp lại theo thứ tự ngược lại và loại bỏ các phần tử. 2) Sử dụng LinkedList thay vì ArrayList. Do các câu hỏi thiên vị được hỏi trong các cuộc phỏng vấn để loại bỏ các bản sao khỏi Danh sách mà không sử dụng bất kỳ bộ sưu tập nào khác, ví dụ trên là câu trả lời. Tuy nhiên, trong thế giới thực, nếu tôi phải đạt được điều này, tôi sẽ đặt các phần tử từ Danh sách sang Tập hợp, đơn giản!


0
/**
     * Method to detect presence of duplicates in a generic list. 
     * Depends on the equals method of the concrete type. make sure to override it as required.
     */
    public static <T> boolean hasDuplicates(List<T> list){
        int count = list.size();
        T t1,t2;

        for(int i=0;i<count;i++){
            t1 = list.get(i);
            for(int j=i+1;j<count;j++){
                t2 = list.get(j);
                if(t2.equals(t1)){
                    return true;
                }
            }
        }
        return false;
    }

Ví dụ về một lớp cụ thể đã ghi đè equals():

public class Reminder{
    private long id;
    private int hour;
    private int minute;

    public Reminder(long id, int hour, int minute){
        this.id = id;
        this.hour = hour;
        this.minute = minute;
    }

    @Override
    public boolean equals(Object other){
        if(other == null) return false;
        if(this.getClass() != other.getClass()) return false;
        Reminder otherReminder = (Reminder) other;
        if(this.hour != otherReminder.hour) return false;
        if(this.minute != otherReminder.minute) return false;

        return true;
    }
}

0
    ArrayList<String> withDuplicates = new ArrayList<>();
    withDuplicates.add("1");
    withDuplicates.add("2");
    withDuplicates.add("1");
    withDuplicates.add("3");
    HashSet<String> set = new HashSet<>(withDuplicates);
    ArrayList<String> withoutDupicates = new ArrayList<>(set);

    ArrayList<String> duplicates = new ArrayList<String>();

    Iterator<String> dupIter = withDuplicates.iterator();
    while(dupIter.hasNext())
    {
    String dupWord = dupIter.next();
    if(withDuplicates.contains(dupWord))
    {
        duplicates.add(dupWord);
    }else{
        withoutDupicates.add(dupWord);
    }
    }
  System.out.println(duplicates);
  System.out.println(withoutDupicates);

Thêm một số giải thích với câu trả lời về cách thức này trả lời giúp đỡ OP trong sửa chữa vấn đề hiện tại
ρяσѕρєя K

0

Câu trả lời này được viết bằng Kotlin, nhưng có thể dễ dàng được dịch sang Java.

Nếu kích thước danh sách mảng của bạn nằm trong một phạm vi nhỏ cố định, thì đây là một giải pháp tuyệt vời.

var duplicateDetected = false
    if(arrList.size > 1){
        for(i in 0 until arrList.size){
            for(j in 0 until arrList.size){
                if(i != j && arrList.get(i) == arrList.get(j)){
                    duplicateDetected = true
                }
            }
        }
    }

0
private boolean isDuplicate() {
    for (int i = 0; i < arrayList.size(); i++) {
        for (int j = i + 1; j < arrayList.size(); j++) {
            if (arrayList.get(i).getName().trim().equalsIgnoreCase(arrayList.get(j).getName().trim())) {
                return true;
            }
        }
    }

    return false;
}
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.