Intersection et union de ArrayLists en Java


130

Existe-t-il des méthodes pour le faire? Je cherchais mais je n'en trouvais pas.

Une autre question: j'ai besoin de ces méthodes pour pouvoir filtrer les fichiers. Certains sont des ANDfiltres et d'autres sont des ORfiltres (comme dans la théorie des ensembles), je dois donc filtrer en fonction de tous les fichiers et des ArrayLists unite / intersecte qui contiennent ces fichiers.

Dois-je utiliser une structure de données différente pour contenir les fichiers? Y a-t-il autre chose qui offrirait une meilleure exécution?


1
Si vous ne souhaitez pas créer une nouvelle liste, Vector.retainAll (Vector) ajuste votre vecteur orignal uniquement à l'intersection avec le deuxième vecteur.
user2808054

@ user2808054 pourquoi Vector? Cette classe est déconseillée depuis Java 1.2.
dimo414 du

@ dimo414 une interface que j'utilise (je n'ai pas d'option) renvoie les choses sous forme de vecteurs. Je ne savais pas que cela avait été découragé! Merci pour l'info .. Découragé par qui? Je n'ai vu aucune note à propos de son
abandon,

1
Des Javadocs: " Depuis la plate-forme Java 2 v1.2 ... il est recommandé d'utiliser ArrayList à la place de Vector. ". Le seul moment dont vous pourriez avoir besoin Vectorest celui des interactions entre threads, mais il existe également des structures de données plus sûres pour ces cas d'utilisation. Voir aussi cette question . Toute bibliothèque encore utilisée Vectoren 2016 est très suspecte à mon avis.
dimo414

@ dimo414 c'est une bibliothèque IBM, haha! (API de données Lotus Domino). Merci pour l'info, très utile
user2808054

Réponses:


122

Voici une implémentation simple sans utiliser de bibliothèque tierce. Le principal avantage par rapport à retainAll, removeAllet addAllc'est que ces méthodes ne modifient pas les listes d'origine entrées dans les méthodes.

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
vous pouvez créer une nouvelle liste avec des éléments list1, puis appeler les méthodes RetainAll, addAll
lukastymo

pourquoi utilisez-vous strictfp dans cette solution?
lukastymo

9
Doit utiliser un HashSetfor intersectionpour que les performances moyennes des cas soient O (n) au lieu de O (n ^ 2).
Zong

1
Cet article pourrait utiliser une mise à jour pour démontrer les avantages de l'API Java 8 Stream.
SME_Dev

J'obtiens une erreur Lorsque j'essaie d'attribuer cette valeur -> Exemple: ArrayList <String> total total = (ArrayList <String>) intersection (list2, list1) ---> impossible de convertir java.util.arraylist en java.util.arraylist < string>
livraison le

123

Collection (donc ArrayList également) ont:

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

Utilisez une implémentation List si vous acceptez les répétitions, une implémentation Set si vous ne le faites pas:

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
Il y a eu une modification suggérée que cette union "est incorrecte car elle contiendra deux fois des éléments communs" . La modification a recommandé d'utiliser un à la HashSetplace.
Kos

5
En fait, il a été édité, voir: "Utilisez une implémentation de liste si vous acceptez les répétitions, une implémentation de Set si vous ne le faites pas:"
lukastymo

7
Non, retentionAll n'est pas une intersection pour la liste. Dans ci-dessus, tous les éléments de col qui ne sont pas dans otherCol sont supprimés. Disons que otherCol est {a, b, b, c} et col est {b, b, b, c, d}. Alors col se termine par {b, b, b, c} qui n'est pas strictement l'intersection des deux. Je m'attendrais à ce que ce soit {b, b, c}. Une opération différente est en cours.
demongolem

1
Je ne vois pas non plus comment addAll()est l'union des listes; il s'agit simplement de concaténer la deuxième liste à la fin de la première. Une opération d'union éviterait d'ajouter un élément si la première liste le contient déjà.
dimo414

66

Ce message est assez ancien, mais c'était néanmoins le premier à apparaître sur Google lors de la recherche de ce sujet.

Je veux donner une mise à jour en utilisant des flux Java 8 faisant (essentiellement) la même chose en une seule ligne:

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

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

Si quelqu'un a une solution meilleure / plus rapide, faites-le moi savoir, mais cette solution est une belle ligne unique qui peut être facilement incluse dans une méthode sans ajouter une classe / méthode d'aide inutile tout en conservant la lisibilité.


19
Ooof, c'est peut-être un bon one-liner mais ça prend du temps O (n ^ 2). Convertissez l'une des listes en un Setpuis utilisez la containsméthode de l'ensemble . Tout dans la vie ne doit pas être fait avec des flux.
dimo414 du

31
list1.retainAll(list2) - is intersection

l'union sera removeAllet alors addAll.

Pour en savoir plus, consultez la documentation de la collection (ArrayList est une collection) http://download.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html


1
Les deux retainAll()et removeAll()sont des opérations O (n ^ 2) sur des listes. On peut faire mieux.
dimo414 du

1
J'ai voté mais maintenant j'ai une question. retainAllsur {1, 2, 2, 3, 4, 5} sur {1, 2, 3} donne {1, 2, 2, 3}. Ne devrait-il pas être {1, 2, 3} pour être l'intersection?
GyuHyeon Choi

21

Unions et intersections définies uniquement pour les ensembles, pas pour les listes. Comme vous l'avez mentionné.

Vérifiez la bibliothèque de goyave pour les filtres. La goyave fournit également de véritables intersections et unions

 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

Vous pouvez utiliser CollectionUtilsdepuis apache commons .


7
Au cas où quelqu'un trouverait cette réponse un peu trop courte: «CollectionUtils.containsAny» et «CollectionUtils.containsAll» sont les méthodes.
Sebastian

2
c'est bizarre que CollectionUtils d'apache commons ne supporte pas les génériques
Vasyl Sarzhynskyi

7

La solution marquée n'est pas efficace. Il a une complexité temporelle O (n ^ 2). Ce que nous pouvons faire est de trier les deux listes et d'exécuter un algorithme d'intersection comme celui ci-dessous.

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

Celui-ci a une complexité de O (n log n + n) qui est en O (n log n). L'union se fait de la même manière. Assurez-vous simplement de faire les modifications appropriées sur les instructions if-elseif-else.

Vous pouvez également utiliser des itérateurs si vous le souhaitez (je sais qu'ils sont plus efficaces en C ++, je ne sais pas si cela est également vrai en Java).


1
Pas assez générique, T peut ne pas être comparable et dans certains cas comparer coûte cher ...
Boris Churzin

Pas générique, je suis totalement d'accord. La comparaison coûte cher? comment résoudriez-vous cela?
AJed le

Malheureusement - ce serait moins cher de le faire en O (n ^ 2) :) Pour les nombres, cette solution est bonne ...
Boris Churzin

Malheureusement, vous n'avez pas répondu à ma question. Permettez-moi de reformuler, en quoi O (n ^ 2) est-il meilleur compte tenu d'une fonction de comparaison du coût c (n)?
AJed le

1
La conversion d'une entrée en un ensemble et l'appel contains()en boucle (comme le suggère Devenv) prendrait un temps O (n + m). Le tri est inutilement compliqué et prend O (n log n + m log n + n) temps. Certes, cela se réduit à O (n log n) temps, mais c'est encore pire que le temps linéaire, et beaucoup plus complexe.
dimo414

4

Je pense que vous devriez utiliser un Setpour contenir les fichiers si vous voulez faire une intersection et une union sur eux. Ensuite, vous pouvez utiliser la classe Sets de Guava pour faire , et filtrer par a également. La différence entre ces méthodes et les autres suggestions est que toutes ces méthodes créent des vues paresseuses de l'union, de l'intersection, etc. des deux ensembles. Apache Commons crée une nouvelle collection et y copie des données. modifie l'une de vos collections en en supprimant des éléments.unionintersectionPredicateretainAll


4

Voici une façon de faire une intersection avec des flux (rappelez-vous que vous devez utiliser java 8 pour les flux):

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 exemple de listes de types différents. Si vous avez une relation entre foo et bar et que vous pouvez obtenir un bar-object de foo, vous pouvez modifier votre flux:

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
  • retentionAll modifiera votre liste
  • Guava n'a pas d'API pour la liste (uniquement pour l'ensemble)

J'ai trouvé ListUtils très utile pour ce cas d'utilisation.

Utilisez ListUtils de org.apache.commons.collections si vous ne souhaitez pas modifier la liste existante.

ListUtils.intersection(list1, list2)


3

Vous pouvez utiliser 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

Dans Java 8, j'utilise des méthodes d'aide simples comme celle-ci:

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

Si les objets de la liste sont hachables (c'est-à-dire ont un hashCode décent et une fonction égale), l'approche la plus rapide entre les tables env. size> 20 consiste à construire un HashSet pour la plus grande des deux listes.

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

Je travaillais également sur une situation similaire et je suis arrivé ici pour chercher de l'aide. J'ai fini par trouver ma propre solution pour les tableaux. ArrayList AbsentDates = new ArrayList (); // Stockera Array1-Array2

Remarque: publiez ceci si cela peut aider quelqu'un à accéder à cette page pour obtenir de l'aide.

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

Intersection de deux listes d'objets différents basés sur une clé commune - 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());
    }

Que diriez-vous de la différence entre ces 2 listes?
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 + (probablement la meilleure performance)

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

Si vous ne vous souciez pas des performances et préférez un code plus petit, utilisez simplement:

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

0

Solution 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

Tout d'abord, je copie toutes les valeurs des tableaux dans un seul tableau, puis je supprime les valeurs en double dans le tableau. Ligne 12, expliquant si le même nombre se produit plus que le temps, mettez une valeur supplémentaire de déchets en position «j». À la fin, parcourez du début à la fin et vérifiez si la même valeur de garbage se produit, puis jetez-la.

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
Bienvenue dans Stack Overflow! Veuillez noter que la question concerne ArrayList. De plus, je crains que cette implémentation particulière laisse à désirer. La valeur 99999999, qui est utilisée comme sentinelle, peut apparaître dans l'entrée. Il serait préférable d'utiliser une structure dynamique, comme ArrayList, pour stocker le résultat de l'union.
SL Barth - Réintégrer Monica le

1
Veuillez expliquer le code que vous avez présenté au lieu d'une simple réponse par code.
tmarois

Je donne juste un indice que vous devez mettre n'importe quelle valeur poubelle
Ashutosh

Je suis content de voir que vous avez ajouté une explication. Malheureusement, la réponse elle-même est toujours mauvaise. Il n'y a aucune raison d'utiliser des tableaux. Vous devez utiliser une structure dynamique comme ArrayList. Si (pour une raison quelconque) vous devez utiliser des tableaux, vous devriez envisager d'utiliser un tableau de Integerplutôt que de int. Ensuite, vous pouvez utiliser à la nullplace de votre "valeur de garbage". Les "valeurs de garbage" ou "valeurs sentinelles" sont généralement une mauvaise idée, car ces valeurs peuvent toujours apparaître dans l'entrée.
SL Barth - Réintégrer Monica le

0

Après les tests, voici ma meilleure approche d'intersection.

Vitesse plus rapide par rapport à l'approche HashSet pure. HashSet et HashMap ci-dessous ont des performances similaires pour les tableaux de plus d'un million d'enregistrements.

En ce qui concerne l'approche Java 8 Stream, la vitesse est assez lente pour une taille de tableau supérieure à 10k.

J'espère que cela peut vous aider.

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

conserverAll (), méthode utilisée pour rechercher un élément commun..ie; intersection list1.retainAll (list2)


-1

Si vous aviez vos données dans des ensembles, vous pourriez utiliser la Setsclasse de Guava .


-1

Si le nombre correspond à celui que je vérifie, il se produit la première fois ou non avec l'aide de "indexOf ()" si le numéro correspond à la première fois, puis imprimez et enregistrez dans une chaîne de sorte que la prochaine fois que le même numéro correspond, il est gagné ' t print car en raison de "indexOf ()", la condition sera fausse.

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
Ne publiez pas simplement le code comme réponse, donnez quelques explications sur ce que vous faites
Brandon Zamudio

c'est mon premier programme que j'ai téléchargé
Ashutosh

2
Bien que ce code puisse aider à résoudre le problème, il n'explique pas pourquoi et / ou comment il répond à la question. Fournir ce contexte supplémentaire améliorerait considérablement sa valeur à long terme. Veuillez modifier votre réponse pour ajouter une explication, y compris les limites et les hypothèses applicables.
Toby Speight
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.