Java: ArrayList'te kopyalar tespit edilsin mi?


104

Bir ArrayList'in Java'da aynı elementlerden birden fazlasını içerip içermediğini nasıl tespit edebilirim (doğru / yanlış döndürür)?

Çok teşekkürler, Terry

Düzenle "Blokları" birbirleriyle değil, tamsayı değerlerini karşılaştırmak istediğimi söylemeyi unuttum. Her "bloğun" bir int'si vardır ve onları farklı kılan da budur. "GetNum" adlı bir yöntemi çağırarak belirli bir Bloğun int değerini buluyorum (örneğin, tablo1 [0] [2] .getNum ();


Eğer "Blok" bir int ile karşılaştırılırsa, muhtemelen hashCode'un aynı int değerine sahip olması ve eşitlerin bu tamsayı karşılaştırması gerekir.
Paul Tomblin

Liste yerine Set kullanın
dmarquina

Yanıtlar:


192

En basit: tüm koleksiyonu bir Set'e boşaltın (Set (Collection) yapıcısını veya Set.addAll kullanarak), ardından Set'in ArrayList ile aynı boyutta olup olmadığına bakın.

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

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

Güncelleme: Sorunuzu doğru anlıyorsam, 2d bir Blok diziniz var.

Blok tablosu [] [];

ve herhangi bir satırında kopya olup olmadığını mı tespit etmek istiyorsunuz?

Bu durumda, Block'un "eşittir" ve "hashCode" u doğru şekilde uyguladığını varsayarak şunları yapabilirim:

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

Sözdizimi için bundan% 100 emin değilim, bu yüzden şu şekilde yazmak daha güvenli olabilir

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.addEklenen öğe zaten set içindeyse bir boolean false döndürür, böylece falsebilmek istediğiniz tek şey herhangi bir kopya olup olmadığı ise , dönen herhangi bir eklemede kısa devre yapabilir ve balyalayabilirsiniz .


13
HashCode / equals'ı da uyguladığınızdan emin olun.
jon077

1
Ya da biraz daha kolay: Küme oluştururken, örneğin addAll yerine yeni HashSet (liste).
Fabian Steeg

2
@ jon077: Bu, "kopya" tanımınıza bağlıdır.
Michael Myers

2D dizideki öğeleri algılama süreci aynı mı olur? Örneğin, [0] [0] dizisinden [0] [6] dizisine (bir 'sıra') kontrol ediliyor ..? Çok teşekkürler, Terry

Dizideki her nesne bir tamsayı değeri tutar. "Çoğalt" ile, nesne aynı tam sayı değerine sahip olacaktır.

60

Set#addListe ve kümenin boyutunu karşılaştırmak yerine dönüş değerini kullanan iyileştirilmiş kod .

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
HashSet'e ne kadar alan ayıracağını söylemek daha verimli olur Set<T> set = new HashSet<T>(list.size());mu? Bir Liste parametresi verildiğinde, listenin yineleme içermemesi yaygınsa daha verimli olacağını düşünüyorum.
Paul Jackson

1
@PaulJackson Boyutlandırma tam listeye göre muhtemelen faydalı olacaktır. Ancak ortak durum, bir kopyasını erken bulması ise, o zaman alan boşa gitmiştir. Ayrıca HashSet, listenin boyutuna göre boyutlandırma bile , karma yapının temelindeki yükleme faktörü nedeniyle tüm liste boyunca çalışırken yeniden boyutlandırmaya neden olacaktır.
Jay Anderson

1
Çalışma zamanı veya alanla ilgili gerçek sorunlar yaşamadığınız sürece, kodunuzu bu şekilde ince ayarlamam. Erken optimizasyondan kaçınılması en iyisidir.
akuhn

15

Yinelemelerden kaçınmak istiyorsanız, yinelenenleri tespit etme sürecini ortadan kaldırmalı ve bir Set kullanmalısınız .



@ jon077: Az önce söylediğim gibi mutlaka değil.
Michael Myers

1
Ancak bir Set kullanmak kopyaları algılamaz . Sadece onları engeller. Elbette yukarıda @akuhn tarafından belirtildiği gibi ekleme yönteminin sonucunu kontrol etmediğiniz sürece.
mcallahan

13

Yinelenen öğeleri döndürmek için iyileştirilmiş kod

  • Bir Koleksiyonda kopyaları bulabilir
  • kopya grubunu iade et
  • Eşsiz Öğeler Setten elde edilebilir

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

Bu oldukça harika. bazı geçersiz kodlarınız var ve belki de en uygun yol bu değil, ancak yaklaşımınız tamamen harika! (ve harika çalışıyor)
Jules Colle

9

Öğeleriniz bir şekilde Karşılaştırılabilir ise (siparişin herhangi bir gerçek anlamı olduğu gerçeği kayıtsızdır - sadece sizin eşitlik tanımınızla tutarlı olması gerekir), en hızlı yinelenen kaldırma çözümü listeyi sıralayacaktır (0 (n log ( n))) sonra tek bir geçiş yapmak ve tekrarlananları aramak için öğeleri (yani, birbirini takip eden eşit öğeler) (bu O (n)).

Genel karmaşıklık O (n log (n)) olacaktır, bu da bir Set ile elde edeceğinizle kabaca aynıdır (n kere uzun (n)), ancak çok daha küçük bir sabitle. Bunun nedeni, sıralama / tekilleştirmedeki sabitin, öğeleri karşılaştırmanın maliyetinden kaynaklanmasıdır, oysa kümedeki maliyetin büyük olasılıkla bir karma hesaplamadan ve ayrıca bir (muhtemelen birkaç) karma karşılaştırmadan kaynaklanmasıdır. Karma tabanlı bir Küme uygulaması kullanıyorsanız, yani Ağaç tabanlı size bir O (n log² (n)) verecektir ki bu daha da kötüsü.

Anladığım kadarıyla, yinelenenleri kaldırmanız gerekmiyor , yalnızca varlığını test etmeniz gerekiyor . Bu nedenle, dizinizde bir birleştirme veya yığın sıralama algoritmasını el ile kodlamalısınız; bu algoritma, karşılaştırıcınız 0 döndürürse yalnızca doğru sonucunu döndüren (yani "bir çift var") ve aksi takdirde sıralamayı tamamlar ve tekrarlar için sıralı dizi testini geçerek çıkar. . Bir birleştirme veya yığın sıralamasında, aslında sıralama tamamlandığında, her iki öğe de son konumlarında olmadıkça (ki bu olası değildir) her yinelenen çifti karşılaştırmış olursunuz. Bu nedenle, ince ayarlı bir sıralama algoritması büyük bir performans artışı sağlamalıdır (bunu kanıtlamam gerekirdi, ancak sanırım ince ayarlı algoritma tekdüze rastgele veriler üzerinde O (log (n)) olmalıdır)


Bu durumda, n 6'dır, bu yüzden uygulama ayrıntıları üzerinde çok fazla zaman kaybetmem, ancak böyle bir şey yapmam gerekirse özel yığın sıralaması hakkındaki fikrinizi saklayacağım.
Paul Tomblin

Üçüncü paragrafı anlamıyorum. Mergesort ve heapsort, yazarken O (log (n)) değil, O (nlog (n)) 'dir; Bir kopya belirlediğinizde çıksanız bile, bu sizin zaman karmaşıklığınızı değiştirmez ...
ChaimKut

8

A için benzer bir işlem yapmam gerekiyordu Streamama iyi bir örnek bulamadım. İşte bulduğum şey.

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

Bu, tüm akışı işlemekten ziyade kopyalar erken bulunduğunda kısa devre yapma avantajına sahiptir ve her şeyi bir içine yerleştirip Setboyutunu kontrol etmekten çok daha karmaşık değildir . Yani bu durum kabaca şöyle olacaktır:

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

7

Java 8+ ile Stream API'yi kullanabilirsiniz:

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

2

Basitçe söylemek gerekirse: 1) tüm öğelerin karşılaştırılabilir olduğundan emin olun 2) diziyi sıralayın 2) dizi üzerinde yineleyin ve kopyaları bulun


1

Bir Listedeki Yinelemeleri bilmek için aşağıdaki kodu kullanın: Size yinelenenleri içeren seti verecektir.

 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

bu sorunu halletmenin en iyi yolu bir HashSet kullanmaktır :

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

Sadece sonuç dizi listesini yazdırın ve sonucu kopyalar olmadan görün :)


1

Yinelenen değerler kümesini istiyorsanız:

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

Ve muhtemelen durumunuza bağlı olarak değerleri kırpmayı veya küçük harf kullanmayı da düşünün.


Yinelemeleri istiyorsanız en basit ve en iyi yanıt, performans için bağımsız değişkenlerin boyutuyla benzersiz set ipucu başlatabilirsiniz.
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
    }

Not: Öğeler listenin başından kaldırıldıkça, bu önemli bir performans düşüşüne neden olacaktır. Bunu ele almak için iki seçeneğimiz var. 1) ters sırada yineleyin ve öğeleri kaldırın. 2) ArrayList yerine LinkedList kullanın. Mülakatlarda kopyaları başka bir koleksiyon kullanmadan Listeden çıkarmak için sorulan önyargılı sorular nedeniyle, cevap yukarıdaki örnektir. Yine de gerçek dünyada, bunu başarmak zorunda kalırsam, Listeden Kümeye öğeler koyacağım, basit!


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

Geçersiz kılınan somut bir sınıf örneği 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);

Bu cevabın şu anki sorunu
çözmede OP'ye

0

Bu cevap Kotlin'de yazılmıştır, ancak kolaylıkla Java'ya çevrilebilir.

Dizi listenizin boyutu sabit bir küçük aralık dahilindeyse, bu harika bir çözümdür.

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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.