Intersezione e unione di ArrayLists in Java


130

Ci sono metodi per farlo? Stavo cercando ma non ne ho trovato.

Un'altra domanda: ho bisogno di questi metodi per poter filtrare i file. Alcuni sono ANDfiltri e altri sono ORfiltri (come nella teoria degli insiemi), quindi ho bisogno di filtrare in base a tutti i file e alle liste di array unite / intersecate che contengono quei file.

Dovrei usare una struttura dati diversa per contenere i file? C'è qualcos'altro che offrirebbe un runtime migliore?


1
Se non si desidera creare un nuovo elenco, Vector.retainAll (Vector) ritaglia il vettore originale solo all'intersezione con il secondo vettore.
user2808054,

@ user2808054 perché Vector? Quella classe è stata scoraggiata da Java 1.2.
dimo414,

@ dimo414 un'interfaccia che sto usando (non ho alcuna opzione) restituisce le cose come vettori. Non sapevo che fosse stato scoraggiato! Grazie per le informazioni .. Scoraggiato da chi? Non ho visto alcuna nota sul fatto che è stato deprecato, quindi questa è una sorpresa
user2808054

1
Da Javadocs: " A partire dalla piattaforma Java 2 v1.2 ... si consiglia di utilizzare ArrayList al posto di Vector. ". L'unica volta che potrebbe essere necessario Vectorè per le interazioni cross-thread, ma ci sono strutture di dati più sicure anche per quei casi d'uso. Vedi anche questa domanda . VectorSecondo me, qualsiasi biblioteca ancora in uso nel 2016 è molto sospetta.
dimo414,

@ dimo414 è una libreria IBM, ahah! (API dati Lotus Domino). Grazie per le informazioni, molto utile
user2808054

Risposte:


122

Ecco un'implementazione semplice senza utilizzare alcuna libreria di terze parti. Vantaggio principale rispetto retainAll, removeAlle addAllè che questi metodi non modificano l'ingresso liste originale per i metodi.

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
puoi creare un nuovo elenco con gli elementi list1 e quindi chiamare keepAll, addAll metodi
lukastymo

perché stai usando strictfp in questa soluzione?
lukastymo,

9
Dovrebbe usare a HashSetfor in intersectionmodo che la prestazione media del case sia O (n) invece di O (n ^ 2).
Zong,

1
Questo post potrebbe utilizzare un aggiornamento per dimostrare i vantaggi dell'API Stream di Java 8.
SME_Dev,

Ottengo un errore quando provo ad assegnare questo valore -> Esempio: ArrayList <String> total total = (ArrayList <String>) intersezione (list2, list1) ---> impossibile eseguire il cast di java.util.arraylist su java.util.arraylist < stringa>
consegna l'

123

Le raccolte (anche ArrayList) hanno:

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

Utilizzare un'implementazione Elenco se si accettano ripetizioni, un'implementazione Imposta se non si:

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
È stata suggerita una modifica secondo cui questa unione "non è corretta poiché conterrà due volte elementi comuni" . La modifica ha raccomandato di utilizzare HashSetinvece un .
Kos,

5
In realtà è stato modificato, vedi: "Usa un'implementazione dell'elenco se accetti le ripetizioni, un'implementazione impostata se non lo fai:"
lukastymo,

7
No, retainAll non è un incrocio per l'elenco. In sopra, tutti gli elementi in col che non sono in otherCol vengono rimossi. Supponiamo che otherCol sia {a, b, b, c} e col sia {b, b, b, c, d}. Quindi col finisce con {b, b, b, c} che non è strettamente l'intersezione dei due. Mi aspetto che sia {b, b, c}. È in corso un'operazione diversa.
demongolem,

1
Inoltre non vedo come addAll()sia l'unione per le liste; sta semplicemente concatenando la seconda lista alla fine della prima. Un'operazione di unione eviterebbe di aggiungere un elemento se il primo elenco lo contiene già.
dimo414,

66

Questo post è piuttosto vecchio, ma tuttavia è stato il primo ad apparire su Google quando cercava quell'argomento.

Voglio dare un aggiornamento usando gli stream Java 8 facendo (sostanzialmente) la stessa cosa in una sola riga:

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

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

Se qualcuno ha una soluzione migliore / più veloce fammelo sapere, ma questa soluzione è una buona soluzione che può essere facilmente inclusa in un metodo senza aggiungere una classe / metodo di supporto non necessari e mantenere comunque la leggibilità.


19
Ooof, potrebbe essere un bel one-liner ma ci vuole O (n ^ 2) tempo. Convertire uno degli elenchi in a Setquindi utilizzare il containsmetodo dell'insieme . Non tutto nella vita deve essere fatto con flussi.
dimo414,

31
list1.retainAll(list2) - is intersection

l'unione sarà removeAlle poiaddAll .

Scopri di più nella documentazione della raccolta (ArrayList è una raccolta) http://download.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html


1
Entrambi retainAll()e removeAll()sono operazioni O (n ^ 2) sugli elenchi. Possiamo fare di meglio.
dimo414,

1
Ho votato ma adesso ho una domanda. retainAlldi {1, 2, 2, 3, 4, 5} su {1, 2, 3} genera {1, 2, 2, 3}. Non dovrebbe essere {1, 2, 3} essere l'intersezione?
GyuHyeon Choi,

21

Unioni e intersezioni definiti solo per insiemi, non elenchi. Come hai menzionato.

Controlla la libreria guava per i filtri. Anche la guava fornisce intersezioni e sindacati reali

 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)

12

Puoi usare CollectionUtilsdai comuni di Apache .


7
Nel caso in cui qualcuno trovi questa risposta un po 'troppo breve: "CollectionUtils.containsAny" e "CollectionUtils.containsAll" sono i metodi.
Sebastian,

2
è strano che CollectionUtils di Apache
Commons

7

La soluzione contrassegnata non è efficiente. Ha una complessità temporale O (n ^ 2). Quello che possiamo fare è ordinare entrambi gli elenchi ed eseguire un algoritmo di intersezione come quello qui sotto.

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

Questo ha una complessità di O (n log n + n) che si trova in O (n log n). L'unione è fatta in modo simile. Assicurati di apportare le opportune modifiche alle istruzioni if-elseif-else.

Puoi anche usare gli iteratori se vuoi (so che sono più efficienti in C ++, non so se ciò sia vero anche in Java).


1
Non abbastanza generico, T potrebbe non essere comparabile e in alcuni casi il confronto è costoso ...
Boris Churzin,

Non generico, sono totalmente d'accordo. Il confronto è costoso? come lo risolveresti?
AJed

Purtroppo - sarebbe più economico farlo in O (n ^ 2) :) Per Numbers questa soluzione è buona ...
Boris Churzin,

Purtroppo, non hai risposto alla mia domanda. Permettetemi di riformularlo, come è meglio O (n ^ 2) data una funzione di confronto di costo c (n)?
AJed,

1
La conversione di un input in un set e la chiamata contains()in un ciclo (come suggerisce Devenv) richiederebbe il tempo O (n + m). L'ordinamento è inutilmente complicato e richiede tempo O (n log n + m log n + n). Certo, ciò riduce il tempo O (n log n), ma è ancora peggio del tempo lineare e molto più complesso.
dimo414,

4

Penso che dovresti usare a Setper conservare i file se vuoi fare intersezione e unione su di essi. Quindi è possibile utilizzare Guava 's set di classe a che fare union, intersectione filtrare da un Predicatepure. La differenza tra questi metodi e gli altri suggerimenti è che tutti questi metodi creano viste pigre sull'unione, l'intersezione, ecc. Dei due insiemi. Apache Commons crea una nuova raccolta e copia i dati su di essa. retainAllcambia una delle tue raccolte rimuovendo elementi da essa.


4

Ecco un modo in cui puoi fare un'intersezione con i flussi (ricorda che devi usare java 8 per i flussi):

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

Un esempio per elenchi con tipi diversi. Se hai una realtà tra foo e bar e puoi ottenere un oggetto bar da foo di quanto puoi modificare il tuo stream:

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
  • retainAll modificherà il tuo elenco
  • Guava non ha API per elenco (solo per set)

Ho trovato ListUtils molto utile per questo caso d'uso.

Utilizzare ListUtils da org.apache.commons.collections se non si desidera modificare l'elenco esistente.

ListUtils.intersection(list1, list2)


3

Puoi usare commons-collections4 CollectionUtils

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

In Java 8, utilizzo semplici metodi di supporto come questo:

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

Se gli oggetti nella lista sono hash (ovvero hanno un hashCode decente e una funzione uguale), l'approccio più veloce tra le tabelle ca. size> 20 serve a costruire un HashSet per la più grande delle due liste.

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

Stavo anche lavorando su una situazione simile e sono arrivato qui in cerca di aiuto. Ho finito per trovare la mia soluzione per gli array. ArrayList AbsentDates = new ArrayList (); // Memorizza Array1-Array2

Nota: pubblicare questo messaggio se può aiutare qualcuno a raggiungere questa pagina per chiedere aiuto.

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

Intersezione di due elenchi di oggetti diversi basati sulla chiave comune - 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());
    }

che ne dite della differenza impostata tra quelle 2 liste?
gennaio

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 + (Probabilmente le migliori prestazioni)

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

Se non ti interessano le prestazioni e preferisci un codice più piccolo, usa semplicemente:

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

0

Soluzione finale:

//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

Innanzitutto, sto copiando tutti i valori degli array in un singolo array, quindi rimuovo i valori duplicati nell'array. Riga 12, che spiega se lo stesso numero si verifica più del tempo, quindi metti un valore di immondizia in più nella posizione "j". Alla fine, attraversa dall'inizio alla fine e controlla se si verifica lo stesso valore di immondizia, quindi scarta.

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
Benvenuto in Stack Overflow! Si noti che la domanda riguarda ArrayList. Inoltre, temo che questa particolare implementazione lasci a desiderare. Il valore 99999999, utilizzato come sentinella, potrebbe verificarsi nell'input. Sarebbe meglio usare una struttura dinamica, come ArrayList, per memorizzare il risultato dell'unione.
SL Barth - Ripristina Monica il

1
Spiega il codice che hai presentato anziché solo una risposta al codice.
tmarois,

Sto solo dando un indizio sul fatto che devi mettere qualsiasi valore di immondizia
Ashutosh,

Sono felice di vedere che hai aggiunto una spiegazione. Sfortunatamente, la risposta stessa è ancora negativa. Non vi è alcun motivo per utilizzare le matrici. È necessario utilizzare una struttura dinamica come ArrayList. Se (per qualche motivo) è necessario utilizzare array, è consigliabile utilizzare un array di Integeranziché int. Quindi è possibile utilizzare al nullposto del "valore immondizia". "Valori di immondizia" o "valori di sentinella" sono generalmente una cattiva idea, poiché questi valori possono ancora verificarsi nell'input.
SL Barth - Ripristina Monica il

0

Dopo il test, ecco il mio miglior approccio all'intersezione.

Velocità più veloce rispetto al puro approccio HashSet. HashSet e HashMap di seguito hanno prestazioni simili per array con oltre 1 milione di record.

Per quanto riguarda l'approccio Java 8 Stream, la velocità è piuttosto lenta per dimensioni di array superiori a 10k.

Spero che questo possa aiutare.

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

Uso del metodo retainAll () per trovare element..ie comune; intersezione list1.retainAll (list2)


-1

Se avessi i tuoi dati in set puoi usare la Setsclasse di Guava .


-1

Se il numero corrisponde a quello che sto verificando, si verifica per la prima volta o meno con l'aiuto di "indexOf ()" se il numero corrisponde per la prima volta, quindi stampa e salva in una stringa in modo che, alla successiva corrispondenza dello stesso numero, vinca " t stampare perché a causa della condizione "indexOf ()" sarà falso.

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
Non limitarti a pubblicare il codice come risposta, dai una piccola spiegazione di cosa stai facendo
Brandon Zamudio,

è il mio primo programma che ho caricato
Ashutosh,

2
Sebbene questo codice possa aiutare a risolvere il problema, non spiega perché e / o come risponde alla domanda. Fornire questo contesto aggiuntivo migliorerebbe significativamente il suo valore a lungo termine. Si prega di modificare la risposta di aggiungere spiegazioni, tra cui quello che si applicano le limitazioni e le assunzioni.
Toby Speight,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.