Java: Mendeteksi duplikat di ArrayList?

104

Bagaimana saya bisa mendeteksi (mengembalikan true / false) apakah ArrayList berisi lebih dari satu elemen yang sama di Java?

Terima kasih banyak, Terry

Sunting Lupa menyebutkan bahwa saya tidak ingin membandingkan "Blok" satu sama lain, tetapi nilai integernya. Setiap "blok" memiliki int dan inilah yang membuatnya berbeda. Saya menemukan int dari Blok tertentu dengan memanggil metode bernama "getNum" (misalnya table1 [0] [2] .getNum ();


sumber
Jika "Block" dibandingkan dengan sebuah int, Anda mungkin harus memiliki hashCode yang mengembalikan int yang sama dan memiliki persamaan untuk membandingkan int tersebut.
Paul Tomblin
gunakan Set alih-alih Daftar
dmarquina

Jawaban:

192

Paling sederhana: buang seluruh koleksi ke dalam Set (menggunakan konstruktor Set (Collection) atau Set.addAll), lalu lihat apakah Set memiliki ukuran yang sama dengan ArrayList.

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

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

Pembaruan: Jika saya memahami pertanyaan Anda dengan benar, Anda memiliki array Blok 2d, seperti pada

Blokir tabel [] [];

dan Anda ingin mendeteksi apakah ada baris yang memiliki duplikat?

Dalam hal ini, saya dapat melakukan hal berikut, dengan asumsi bahwa Block mengimplementasikan "sama dengan" dan "hashCode" dengan benar:

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

Saya tidak 100% yakin tentang sintaks, jadi mungkin lebih aman untuk menuliskannya sebagai

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.addmengembalikan boolean false jika item yang ditambahkan sudah ada di set, sehingga Anda bahkan dapat melakukan short circuit dan bale pada add yang mengembalikan falsejika semua yang ingin Anda ketahui adalah apakah ada duplikat.

Paul Tomblin
sumber
13
Pastikan untuk menerapkan hashCode / equals juga.
jon077
1
Atau bahkan sedikit lebih mudah: bungkus saat membuat set, misalnya HashSet baru (daftar), daripada menggunakan addAll.
Fabian Steeg
2
@ jon077: Itu tergantung pada definisi Anda tentang "duplikat".
Michael Myers
Akankah proses mendeteksi elemen dalam larik 2D akan sama? Misalnya, memeriksa dari larik [0] [0] ke larik [0] [6] (sebuah 'baris') ..? Terima kasih banyak, Terry
Setiap objek dalam larik memiliki nilai integer. Dengan "duplikat", objek akan memiliki nilai bilangan bulat yang sama.
60

Kode yang ditingkatkan, menggunakan nilai kembalian Set#adddaripada membandingkan ukuran daftar dan set.

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;
}
akuhn
sumber
7
Apakah akan lebih efisien untuk memberitahu HashSet berapa banyak ruang untuk mengalokasikan: Set<T> set = new HashSet<T>(list.size());? Dengan adanya parameter List, menurut saya akan lebih efisien jika biasanya list tidak mengandung duplikat.
Paul Jackson
1
@PaulJackson Mengukur berdasarkan daftar lengkap mungkin akan bermanfaat. Namun jika kasus umum adalah menemukan duplikat lebih awal maka ruang itu terbuang percuma. Juga bahkan mengubah ukuran HashSetke ukuran daftar akan mengakibatkan pengubahan ukuran saat menjalankan seluruh daftar karena faktor pemuatan yang mendasari struktur hash.
Jay Anderson
1
Kecuali Anda mengalami masalah aktual dengan runtime atau ruang, saya tidak akan menyempurnakan kode Anda seperti itu. Optimasi dini sebaiknya dihindari.
akuhn
15

Jika Anda ingin menghindari duplikat sama sekali, Anda harus memotong proses tengah pendeteksian duplikat dan menggunakan Set .

matt b
sumber
1
Pastikan untuk mengimplementasikan hashCode / sama dengan :)
jon077
@ jon077: Belum tentu, seperti yang baru saja saya katakan.
Michael Myers
1
Namun menggunakan Set tidak mendeteksi duplikat. Itu hanya mencegah mereka. Kecuali tentu saja Anda memeriksa hasil dari metode tambah seperti yang dicatat oleh @akuhn di atas.
mcallahan
13

Kode yang ditingkatkan untuk mengembalikan elemen duplikat

  • Dapat menemukan duplikat dalam Koleksi
  • mengembalikan kumpulan duplikat
  • Elemen Unik dapat diperoleh dari Set

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;
}
pengguna60062
sumber
Itu sangat mengagumkan. Anda memiliki beberapa kode yang tidak valid, dan mungkin itu bukan cara yang paling optimal, tetapi pendekatan Anda benar-benar hebat! (dan itu bekerja dengan baik)
Jules Colle
9

Jika elemen Anda entah bagaimana Dapat Dibandingkan (fakta bahwa urutan memiliki arti sebenarnya adalah acuh tak acuh - itu hanya perlu konsisten dengan definisi persamaan Anda), solusi penghapusan duplikat tercepat adalah mengurutkan daftar (0 (n log ( n))) kemudian melakukan single pass dan mencari elemen berulang (yaitu, elemen sama yang mengikuti satu sama lain) (ini adalah O (n)).

Kompleksitas keseluruhan akan menjadi O (n log (n)), yang kira-kira sama dengan apa yang akan Anda dapatkan dengan Himpunan (n kali panjang (n)), tetapi dengan konstanta yang jauh lebih kecil. Ini karena konstanta dalam pengurutan / dedup dihasilkan dari biaya elemen pembanding, sedangkan biaya dari kumpulan kemungkinan besar dihasilkan dari komputasi hash, ditambah satu (mungkin beberapa) perbandingan hash. Jika Anda menggunakan implementasi Set berbasis hash, itu karena berbasis Tree akan memberi Anda O (n log² (n)), yang bahkan lebih buruk.

Namun, seperti yang saya pahami, Anda tidak perlu menghapus duplikat, tetapi hanya menguji keberadaannya. Jadi, Anda harus membuat kode sendiri untuk algoritme gabungan atau penyortiran heap pada larik Anda, yang hanya keluar dengan mengembalikan true (yaitu "ada dup") jika pembanding Anda mengembalikan 0, dan sebaliknya menyelesaikan pengurutan, dan melintasi pengujian larik yang diurutkan untuk pengulangan . Dalam penggabungan atau penyortiran heap, memang, saat pengurutan selesai, Anda akan membandingkan setiap pasangan duplikat kecuali jika kedua elemen sudah berada di posisi akhirnya (yang tidak mungkin). Dengan demikian, algoritme pengurutan yang diubah harus menghasilkan peningkatan kinerja yang besar (saya harus membuktikannya, tetapi saya kira algoritme yang diubah harus ada di O (log (n)) pada data acak yang seragam)

Varkhan
sumber
Dalam hal ini, n adalah 6 jadi saya tidak akan membuang banyak waktu untuk detail implementasi, tapi saya akan menyimpan ide Anda tentang jenis heap khusus jika saya perlu melakukan sesuatu seperti itu.
Paul Tomblin
Saya tidak mengerti paragraf ketiga. Mergesort dan heapsort keduanya adalah O (nlog (n)), bukan O (log (n)) saat Anda menulis; bahkan jika Anda keluar setelah Anda mengidentifikasi duplikat, itu tetap tidak mengubah kerumitan waktu Anda ...
ChaimKut
8

Saya perlu melakukan operasi serupa untuk a Stream, tetapi tidak dapat menemukan contoh yang baik. Inilah yang saya dapatkan.

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

Ini memiliki keuntungan dari hubungan arus pendek ketika duplikat ditemukan lebih awal daripada harus memproses seluruh aliran dan tidak jauh lebih rumit daripada hanya memasukkan semuanya ke dalam Setdan memeriksa ukurannya. Jadi kasus ini kira-kira menjadi:

List<T> list = ...
boolean allDistinct = areUnique(list.stream());
Jay Anderson
sumber
7

Dengan Java 8+ Anda dapat menggunakan Stream API:

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

Sederhananya: 1) pastikan semua item sebanding 2) urutkan array 2) ulangi array dan temukan duplikat

Antonio
sumber
1

Untuk mengetahui Duplikat dalam Daftar gunakan kode berikut: Ini akan memberi Anda himpunan yang berisi duplikat.

 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;
  }
Rakesh Sabbani
sumber
1

cara terbaik untuk menangani masalah ini adalah dengan menggunakan 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);

Cukup cetak daftar larik hasil dan lihat hasilnya tanpa duplikat :)

Ashana.Jackol
sumber
1

Jika Anda menginginkan kumpulan nilai duplikat:

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);
    }
}

Dan mungkin juga berpikir tentang memangkas nilai atau menggunakan huruf kecil ... tergantung pada kasus Anda.

Saurabh
sumber
Jawaban paling sederhana dan terbaik jika Anda menginginkan duplikat, untuk kinerja Anda dapat memberikan petunjuk uniqueSet dengan ukuran 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
    }

Catatan: ini akan memiliki kinerja yang besar meskipun item dihapus dari awal daftar. Untuk mengatasi ini, kami memiliki dua opsi. 1) ulangi dalam urutan terbalik dan hapus elemen. 2) Gunakan LinkedList, bukan ArrayList. Karena pertanyaan bias yang diajukan dalam wawancara untuk menghapus duplikat dari Daftar tanpa menggunakan koleksi lain, contoh di atas adalah jawabannya. Namun di dunia nyata, jika saya harus mencapai ini, saya akan meletakkan elemen dari List ke Set, sederhana!

Amitesh Jha
sumber
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;
    }

Contoh kelas beton yang telah diganti 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;
    }
}
faizal
sumber
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);
Venkata
sumber
Tambahkan beberapa penjelasan dengan jawaban bagaimana jawaban ini membantu OP dalam memperbaiki masalah saat ini
ρяσѕρєя K
0

Jawaban ini ditulis di Kotlin, tetapi dapat dengan mudah diterjemahkan ke Java.

Jika ukuran daftar larik Anda berada dalam kisaran kecil yang tetap, maka ini adalah solusi yang bagus.

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
                }
            }
        }
    }
grantespo
sumber
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;
}
Ketan Ramani
sumber