Java'da ArrayLists'in kesişimi ve birleşimi


130

Bunu yapmak için herhangi bir yöntem var mı? Arıyordum ama bulamadım.

Başka bir soru: Dosyaları filtreleyebilmek için bu yöntemlere ihtiyacım var. Bazıları ANDfiltreler ve bazıları ORfiltrelerdir (küme teorisindeki gibi), bu yüzden tüm dosyalara ve bu dosyaları tutan Unite / Interersects ArrayLists'e göre filtrelemem gerekiyor.

Dosyaları tutmak için farklı bir veri yapısı kullanmalı mıyım? Daha iyi bir çalışma süresi sunacak başka bir şey var mı?


1
Yeni bir liste oluşturmak istemediyseniz, Vector.retainAll (Vector) orijinal vektörünüzü yalnızca ikinci vektörle kesişme noktasında kırpar.
user2808054

@ user2808054 neden Vector? Bu sınıf Java 1.2'den beri tavsiye edilmiyor.
dimo414

@ dimo414 kullandığım bir arayüz (seçeneğim yok) şeyleri vektör olarak döndürüyor. Cesaretimin kırıldığını bilmiyordum! Bilgi için teşekkürler .. Kim cesaretini kırdı? Kullanımdan kaldırıldığına dair herhangi bir not görmedim, bu yüzden bu bir sürpriz
user2808054

1
Javadocs'tan: " Java 2 platformu v1.2'den itibaren ... Vector yerine ArrayList kullanılması önerilir. ". İhtiyacınız olabilecek tek zaman Vector, iş parçacıkları arası etkileşimler içindir, ancak bu kullanım durumları için de daha güvenli veri yapıları vardır. Ayrıca bu soruya bakın . Vector2016 yılında halen kullanılan herhangi bir kütüphane bence çok şüpheli.
dimo414

@ dimo414 bir IBM kitaplığı, haha! (Lotus Domino veri api). Bilgi için teşekkürler, çok yardımcı oldu
user2808054

Yanıtlar:


122

Burada herhangi bir üçüncü taraf kitaplığı kullanmadan sade bir uygulama var. Ana avantaj üzerinde retainAll, removeAllve addAllbu yöntemler yöntemlere orijinal listeler girişi değiştirmek kalmamasıdır.

public class Test {

    public static void main(String... args) throws Exception {

        List<String> list1 = new ArrayList<String>(Arrays.asList("A", "B", "C"));
        List<String> list2 = new ArrayList<String>(Arrays.asList("B", "C", "D", "E", "F"));

        System.out.println(new Test().intersection(list1, list2));
        System.out.println(new Test().union(list1, list2));
    }

    public <T> List<T> union(List<T> list1, List<T> list2) {
        Set<T> set = new HashSet<T>();

        set.addAll(list1);
        set.addAll(list2);

        return new ArrayList<T>(set);
    }

    public <T> List<T> intersection(List<T> list1, List<T> list2) {
        List<T> list = new ArrayList<T>();

        for (T t : list1) {
            if(list2.contains(t)) {
                list.add(t);
            }
        }

        return list;
    }
}

16
list1 öğeleriyle yeni bir liste oluşturabilir ve ardından keepAll, addAll yöntemleri
lukastymo

Bu çözümde neden tightfp kullanıyorsunuz?
lukastymo

9
Bir kullanmalıdır HashSetiçin intersectionortalama durum performansı (n ^ 2) O (n) yerine, O, böylece.
Zong

1
Bu gönderi, Java 8 Stream API'nin faydalarını göstermek için bir güncelleme kullanabilir.
SME_Dev

Bu değeri atamayı denediğimde hata alıyorum -> Örnek: ArrayList <String> total total = (ArrayList <String>) intersection (list2, list1) ---> java.util.arraylist'i java.util.arraylist'e çeviremiyorum < string>
2016

123

Koleksiyon (yani ArrayList de) şunları içerir:

col.retainAll(otherCol) // for intersection
col.addAll(otherCol) // for union

Tekrarları kabul ediyorsanız bir Liste uygulaması, istemiyorsanız Set uygulaması kullanın:

Collection<String> col1 = new ArrayList<String>(); // {a, b, c}
// Collection<String> col1 = new TreeSet<String>();
col1.add("a");
col1.add("b");
col1.add("c");

Collection<String> col2 = new ArrayList<String>(); // {b, c, d, e}
// Collection<String> col2 = new TreeSet<String>();
col2.add("b");
col2.add("c");
col2.add("d");
col2.add("e");

col1.addAll(col2);
System.out.println(col1); 
//output for ArrayList: [a, b, c, b, c, d, e]
//output for TreeSet: [a, b, c, d, e]

3
Bu birleşimin "ortak öğeleri iki kez içereceği için yanlış" olduğuna dair önerilen bir düzenleme var . Düzenleme HashSetyerine a kullanılması önerilir .
Kos

5
Aslında düzenlenmiştir, bkz: "Tekrarları kabul ediyorsanız bir Liste uygulaması kullanın, istemiyorsanız bir Set uygulaması kullanın:"
lukastymo

7
Hayır, keepAll, listenin kesişim noktası değildir. Yukarıda, col içindeki otherCol'da olmayan tüm öğeler kaldırılır. OtherCol {a, b, b, c} ve col {b, b, b, c, d} diyelim. Daha sonra col, ikisinin kesin olarak kesişimi olmayan {b, b, b, c} ile biter. Bunun {b, b, c} olmasını beklerdim. Farklı bir işlem gerçekleştiriliyor.
demongolem

1
addAll()Listeler için birliğin nasıl olduğunu da anlamıyorum ; sadece ikinci listeyi birincinin sonuna birleştiriyor. Bir birleşim işlemi, ilk liste onu zaten içeriyorsa bir öğe eklemekten kaçınır.
dimo414

66

Bu gönderi oldukça eskidir, ancak yine de bu konuyu ararken Google'da ilk ortaya çıkan oydu.

Aynı şeyi (temelde) tek bir satırda yapan Java 8 akışlarını kullanarak bir güncelleme vermek istiyorum:

List<T> intersect = list1.stream()
    .filter(list2::contains)
    .collect(Collectors.toList());

List<T> union = Stream.concat(list1.stream(), list2.stream())
    .distinct()
    .collect(Collectors.toList());

Herhangi birinin daha iyi / daha hızlı bir çözümü varsa bana bildirin, ancak bu çözüm, gereksiz bir yardımcı sınıf / yöntem eklemeden kolayca bir yönteme dahil edilebilen ve okunabilirliği koruyabilen hoş bir çözümdür.


19
Ooof, güzel bir tek satırlık olabilir ama O (n ^ 2) zaman alır. Listelerden birini a'ya dönüştürün ve Setardından kümenin containsyöntemini kullanın . Hayattaki her şeyin akışlarla yapılması gerekmiyor.
dimo414

31
list1.retainAll(list2) - is intersection

sendika olacak removeAllve sonra addAll.

Koleksiyon belgelerinde daha fazlasını bulun (ArrayList bir koleksiyondur) http://download.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html


1
Her ikisi retainAll()ve removeAll()listelerde O (n ^ 2) işlemleridir. Daha iyisini yapabiliriz.
dimo414

1
Oy verdim ama şimdi bir sorum var. retainAll{1, 2, 3} üzerinden {1, 2, 2, 3, 4, 5} sayısı {1, 2, 2, 3} ile sonuçlanır. Kavşak olması {1, 2, 3} olması gerekmiyor mu?
GyuHyeon Choi

21

Listeler için değil, yalnızca kümeler için tanımlanan birleşimler ve kesişimler. Bahsettiğin gibi.

Filtreler için guava kitaplığını kontrol edin . Ayrıca guava gerçek kavşaklar ve birleşimler sağlar

 static <E> Sets.SetView<E >union(Set<? extends E> set1, Set<? extends E> set2)
 static <E> Sets.SetView<E> intersection(Set<E> set1, Set<?> set2)


7

İşaretlenen çözüm verimli değil. O (n ^ 2) zaman karmaşıklığına sahiptir. Yapabileceğimiz şey, her iki listeyi de sıralamak ve aşağıdaki gibi bir kesişim algoritması çalıştırmaktır.

private  static ArrayList<Integer> interesect(ArrayList<Integer> f, ArrayList<Integer> s) { 
    ArrayList<Integer> res = new ArrayList<Integer>();

    int i = 0, j = 0; 
    while (i != f.size() && j != s.size()) { 

        if (f.get(i) < s.get(j)) {
            i ++;
        } else if (f.get(i) > s.get(j)) { 
            j ++;
        } else { 
            res.add(f.get(i)); 
            i ++;  j ++;
        }
    }


    return res; 
}

Bu, O (n log n) içinde olan O (n log n + n) karmaşıklığına sahiptir. Sendika da benzer şekilde yapılır. Sadece if-elseif-else ifadelerinde uygun değişiklikleri yaptığınızdan emin olun.

İsterseniz yineleyicileri de kullanabilirsiniz (C ++ 'da daha verimli olduklarını biliyorum, bunun Java'da da doğru olup olmadığını bilmiyorum).


1
Yeterince genel değil, T Karşılaştırılabilir olmayabilir ve bazı durumlarda karşılaştırma pahalı olabilir ...
Boris Churzin

Genel değil, tamamen katılıyorum. Karşılaştırma pahalı mı? bunu nasıl çözerdin?
2016

Ne yazık ki - bunu O (n ^ 2)
dilinde

Ne yazık ki soruma cevap vermedin. Yeniden ifade edeyim, maliyet c (n) 'nin bir karşılaştırma fonksiyonu verildiğinde O (n ^ 2) nasıl daha iyidir?
2016

1
Bir girişi bir kümeye dönüştürmek ve contains()bir döngü içinde çağırmak (Devenv'in önerdiği gibi) O (n + m) zaman alacaktır. Sıralama gereksiz yere karmaşıktır ve O (n log n + m log n + n) süresi alır. O (n log n) zamana düştüğü kabul edildi, ancak bu yine de doğrusal zamandan daha kötü ve çok daha karmaşık.
dimo414

4

SetDosyaları birleştirme ve kesişme yapmak istiyorsanız, dosyaları tutmak için a kullanmanız gerektiğini düşünüyorum . Sonra kullanabilirsiniz Guava 'ın ayarlar sınıf yapmak union, intersectionbir tarafından ve filtreleme Predicatesıra. Bu yöntemler ile diğer öneriler arasındaki fark, tüm bu yöntemlerin iki setin birleşimi, kesişimi vb. Hakkında tembel görünümler oluşturmasıdır . Apache Commons yeni bir koleksiyon oluşturur ve buna veri kopyalar. retainAllkoleksiyonlarınızdan birini ondan öğe kaldırarak değiştirir.


4

İşte akışlarla kesişme yapmanın bir yolu (akışlar için java 8 kullanmanız gerektiğini unutmayın):

List<foo> fooList1 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<foo> fooList2 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
fooList1.stream().filter(f -> fooList2.contains(f)).collect(Collectors.toList());

Farklı türlere sahip listeler için bir örnek. Foo ve bar arasında bir ilişki varsa ve akışınızı değiştirebileceğinizden foo'dan bir bar nesnesi alabiliyorsanız:

List<foo> fooList = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<bar> barList = new ArrayList<>(Arrays.asList(new bar(), new bar()));

fooList.stream().filter(f -> barList.contains(f.getBar()).collect(Collectors.toList());

3
  • KeepAll listenizi değiştirecek
  • Guava'nın Liste için API'leri yok (sadece set için)

ListUtils'i bu kullanım durumu için çok yararlı buldum.

Mevcut listeyi değiştirmek istemiyorsanız, org.apache.commons.collections'daki ListUtils'i kullanın.

ListUtils.intersection(list1, list2)


3

Commons-collections4 CollectionUtils'i kullanabilirsiniz

Collection<Integer> collection1 = Arrays.asList(1, 2, 4, 5, 7, 8);
Collection<Integer> collection2 = Arrays.asList(2, 3, 4, 6, 8);

Collection<Integer> intersection = CollectionUtils.intersection(collection1, collection2);
System.out.println(intersection); // [2, 4, 8]

Collection<Integer> union = CollectionUtils.union(collection1, collection2);
System.out.println(union); // [1, 2, 3, 4, 5, 6, 7, 8]

Collection<Integer> subtract = CollectionUtils.subtract(collection1, collection2);
System.out.println(subtract); // [1, 5, 7]

2

Java 8'de bunun gibi basit yardımcı yöntemler kullanıyorum:

public static <T> Collection<T> getIntersection(Collection<T> coll1, Collection<T> coll2){
    return Stream.concat(coll1.stream(), coll2.stream())
            .filter(coll1::contains)
            .filter(coll2::contains)
            .collect(Collectors.toSet());
}

public static <T> Collection<T> getMinus(Collection<T> coll1, Collection<T> coll2){
    return coll1.stream().filter(not(coll2::contains)).collect(Collectors.toSet());
}

public static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
}

1

Listedeki nesneler hashable ise (yani iyi bir hashCode ve equals işlevine sahipse), tablolar arasındaki en hızlı yaklaşım yakl. size> 20, iki listeden daha büyük olan için bir HashSet oluşturmaktır.

public static <T> ArrayList<T> intersection(Collection<T> a, Collection<T> b) {
    if (b.size() > a.size()) {
        return intersection(b, a);
    } else {
        if (b.size() > 20 && !(a instanceof HashSet)) {
            a = new HashSet(a);
        }
        ArrayList<T> result = new ArrayList();
        for (T objb : b) {
            if (a.contains(objb)) {
                result.add(objb);
            }
        }
        return result;
    }
}

1

Ben de benzer durum üzerinde çalışıyordum ve yardım arayarak buraya geldim. Diziler için kendi çözümümü buldum. ArrayList AbsentDates = new ArrayList (); // Dizi1-Dizi2 depolanacak

Not: Birinin yardım için bu sayfaya ulaşmasına yardımcı olacaksa bunu yayınlayın.

ArrayList<String> AbsentDates = new ArrayList<String>();//This Array will store difference
      public void AbsentDays() {
            findDates("April", "2017");//Array one with dates in Month April 2017
            findPresentDays();//Array two carrying some dates which are subset of Dates in Month April 2017

            for (int i = 0; i < Dates.size(); i++) {

                for (int j = 0; j < PresentDates.size(); j++) {

                    if (Dates.get(i).equals(PresentDates.get(j))) {

                        Dates.remove(i);
                    }               

                }              
                AbsentDates = Dates;   
            }
            System.out.println(AbsentDates );
        }

1

Ortak anahtara dayalı iki farklı nesne listesinin kesişimi - Java 8

 private List<User> intersection(List<User> users, List<OtherUser> list) {

        return list.stream()
                .flatMap(OtherUser -> users.stream()
                        .filter(user -> user.getId()
                                .equalsIgnoreCase(OtherUser.getId())))
                .collect(Collectors.toList());
    }

bu 2 liste arasındaki farka ne dersiniz?
Jean

1
public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
    Set<T> set1, set2;
    if (col1 instanceof Set) {
        set1 = (Set) col1;
    } else {
        set1 = new HashSet<>(col1);
    }

    if (col2 instanceof Set) {
        set2 = (Set) col2;
    } else {
        set2 = new HashSet<>(col2);
    }

    Set<T> intersection = new HashSet<>(Math.min(set1.size(), set2.size()));

    for (T t : set1) {
        if (set2.contains(t)) {
            intersection.add(t);
        }
    }

    return intersection;
}

JDK8 + (Muhtemelen En İyi Performans)

public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
    boolean isCol1Larger = col1.size() > col2.size();
    Set<T> largerSet;
    Collection<T> smallerCol;

    if (isCol1Larger) {
        if (col1 instanceof Set) {
            largerSet = (Set<T>) col1;
        } else {
            largerSet = new HashSet<>(col1);
        }
        smallerCol = col2;
    } else {
        if (col2 instanceof Set) {
            largerSet = (Set<T>) col2;
        } else {
            largerSet = new HashSet<>(col2);
        }
        smallerCol = col1;
    }

    return smallerCol.stream()
            .filter(largerSet::contains)
            .collect(Collectors.toSet());
}

Performansı önemsemiyorsanız ve daha küçük kodları tercih ediyorsanız, yalnızca şunu kullanın:

col1.stream().filter(col2::contains).collect(Collectors.toList());

0

Son çözüm:

//all sorted items from both
public <T> List<T> getListReunion(List<T> list1, List<T> list2) {
    Set<T> set = new HashSet<T>();
    set.addAll(list1);
    set.addAll(list2);
    return new ArrayList<T>(set);
}

//common items from both
public <T> List<T> getListIntersection(List<T> list1, List<T> list2) {
    list1.retainAll(list2);
    return list1;
}

//common items from list1 not present in list2
public <T> List<T> getListDifference(List<T> list1, List<T> list2) {
    list1.removeAll(list2);
    return list1;
}

0

İlk olarak, dizilerin tüm değerlerini tek bir diziye kopyalıyorum, sonra yinelenen değerleri diziye kaldırıyorum. Satır 12, aynı sayının zamandan fazla ortaya çıkıp çıkmadığını açıklayan daha sonra fazladan bir çöp değerini "j" konumuna koyun. Sonunda, baştan sona geçiş yapın ve aynı anlamsız değerin oluşup oluşmadığını kontrol edin ve ardından atın.

public class Union {
public static void main(String[] args){

    int arr1[]={1,3,3,2,4,2,3,3,5,2,1,99};
    int arr2[]={1,3,2,1,3,2,4,6,3,4};
    int arr3[]=new int[arr1.length+arr2.length];

    for(int i=0;i<arr1.length;i++)
        arr3[i]=arr1[i];

    for(int i=0;i<arr2.length;i++)
        arr3[arr1.length+i]=arr2[i];
    System.out.println(Arrays.toString(arr3));

    for(int i=0;i<arr3.length;i++)
    {
        for(int j=i+1;j<arr3.length;j++)
        {
            if(arr3[i]==arr3[j])
                arr3[j]=99999999;          //line  12
        }
    }
    for(int i=0;i<arr3.length;i++)
    {
        if(arr3[i]!=99999999)
            System.out.print(arr3[i]+" ");
    }
}   
}

1
Stack Overflow'a hoş geldiniz! Lütfen sorunun ArrayList ile ilgili olduğunu unutmayın. Ayrıca, korkarım bu özel uygulama, istenen şeyleri bırakıyor. Girişte gözcü olarak kullanılan 99999999 değeri ortaya çıkabilir. ArrayListBirliğin sonucunu saklamak için dinamik bir yapı kullanmak daha iyi olacaktır .
SL Barth - Monica'yı eski

1
Lütfen yalnızca bir kod cevabı yerine sunduğunuz kodu açıklayın.
tmarois

Herhangi bir çöp değeri koymanız gerektiğine dair bir ipucu veriyorum
Ashutosh

Bir açıklama eklemiş olmanıza sevindim. Maalesef cevabın kendisi hala kötü. Dizi kullanmak için hiçbir sebep yok. ArrayList gibi dinamik bir yapı kullanmalısınız. Eğer (herhangi bir nedenle) dizi kullanmanız gerekiyorsa, Integeryerine bir dizi kullanmayı düşünmelisiniz int. O zaman null"çöp değeriniz" yerine kullanabilirsiniz . "Garbage değerler" veya "sentinel değerler" genellikle kötü bir fikirdir, çünkü bu değerler yine de girdide olabilir.
SL Barth - Monica'yı eski

0

Test ettikten sonra, işte en iyi kavşak yaklaşımım.

Saf HashSet Yaklaşımına kıyasla daha hızlı hız. Aşağıdaki HashSet ve HashMap, 1 milyondan fazla kaydı olan diziler için benzer performansa sahiptir.

Java 8 Stream yaklaşımına gelince, 10k'den büyük dizi boyutu için hız oldukça yavaştır.

Umarım bu yardımcı olabilir.

public static List<String> hashMapIntersection(List<String> target, List<String> support) {
    List<String> r = new ArrayList<String>();
    Map<String, Integer> map = new HashMap<String, Integer>();
    for (String s : support) {
        map.put(s, 0);
    }
    for (String s : target) {
        if (map.containsKey(s)) {
            r.add(s);
        }
    }
    return r;
}
public static List<String> hashSetIntersection(List<String> a, List<String> b) {
    Long start = System.currentTimeMillis();

    List<String> r = new ArrayList<String>();
    Set<String> set = new HashSet<String>(b);

    for (String s : a) {
        if (set.contains(s)) {
            r.add(s);
        }
    }
    print("intersection:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
    return r;
}

public static void union(List<String> a, List<String> b) {
    Long start = System.currentTimeMillis();
    Set<String> r= new HashSet<String>(a);
    r.addAll(b);
    print("union:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
}

0

keepAll () yöntemi, ortak öğeyi bulmak için kullanılır..ie; kesişim list1.retainAll (list2)


-1

Verileriniz Setlerde olsaydı, Guava'nın Setssınıfını kullanabilirsiniz .


-1

Numara, kontrol ettiğimden daha eşleşirse, "indexOf ()" yardımıyla ilk kez oluşup oluşmuyorsa, numara ilk kez eşleşiyorsa, yazdırın ve bir dizeye kaydedin, böylece bir sonraki sefer aynı sayı eşleştiğinde o kazanır ' t yazdırın çünkü "indexOf ()" koşulu yanlış olacaktır.

class Intersection
{
public static void main(String[] args)
 {
  String s="";
    int[] array1 = {1, 2, 5, 5, 8, 9, 7,2,3512451,4,4,5 ,10};
    int[] array2 = {1, 0, 6, 15, 6, 5,4, 1,7, 0,5,4,5,2,3,8,5,3512451};


       for (int i = 0; i < array1.length; i++)
       {
           for (int j = 0; j < array2.length; j++)
           {
               char c=(char)(array1[i]);
               if(array1[i] == (array2[j])&&s.indexOf(c)==-1)
               {    
                System.out.println("Common element is : "+(array1[i]));
                s+=c;
                }
           }
       }    
}

}


2
Kodu yanıt olarak göndermekle yetinmeyin, ne yaptığınızla ilgili küçük bir açıklama yapın
Brandon Zamudio

yüklediğim ilk programım
Ashutosh

2
Bu kod sorunu çözmeye yardımcı olsa da soruyu neden ve / veya nasıl yanıtladığını açıklamaz. Bu ek bağlamın sağlanması, uzun vadeli değerini önemli ölçüde artıracaktır. Hangi sınırlamalar ve varsayımların geçerli olduğu dahil açıklama eklemek için lütfen yanıtınızı düzenleyin .
Toby Speight
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.