Intersección y unión de ArrayLists en Java


130

¿Hay algún método para hacerlo? Estaba buscando pero no pude encontrar ninguno.

Otra pregunta: necesito estos métodos para poder filtrar archivos. Algunos son ANDfiltros y otros son ORfiltros (como en la teoría de conjuntos), por lo que necesito filtrar de acuerdo con todos los archivos y las ArrayLists de unión / intersección que contienen esos archivos.

¿Debo usar una estructura de datos diferente para guardar los archivos? ¿Hay algo más que ofrezca un mejor tiempo de ejecución?


1
Si no desea crear una nueva lista, Vector.retainAll (Vector) recorta su vector original solo a la intersección con el segundo vector.
user2808054

@ user2808054 ¿por qué Vector? Esa clase ha sido desalentada desde Java 1.2.
dimo414

@ dimo414 una interfaz que estoy usando (no tengo opción) devuelve cosas como vectores. ¡No sabía que se había desanimado! Gracias por la información .. ¿Desalentado por quién? No he visto ninguna nota sobre su desaprobación, así que esto es una sorpresa
Usuario2808054

1
De los Javadocs: " A partir de la plataforma Java 2 v1.2 ... se recomienda utilizar ArrayList en lugar de Vector ". El único momento que puede necesitar Vectores para interacciones entre hilos, pero también hay estructuras de datos más seguras para esos casos de uso. Ver también esta pregunta . Cualquier biblioteca que todavía use Vectoren 2016 es muy sospechosa en mi opinión.
dimo414

@ dimo414 es una biblioteca de IBM, jaja! (API de datos de Lotus Domino). Gracias por la información, muy útil
User2808054

Respuestas:


122

Aquí hay una implementación simple sin usar ninguna biblioteca de terceros. Principal ventaja sobre retainAll, removeAlly addAlles que estos métodos no modifican la entrada de las listas originales de los métodos.

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
puede crear una nueva lista con elementos list1 y luego llamar a los métodos
Retener a todos

¿Por qué usas strictlyfp en esta solución?
lukastymo

9
Debe usar un HashSetfor intersectionpara que el rendimiento promedio del caso sea O (n) en lugar de O (n ^ 2).
Zong

1
Esta publicación podría usar una actualización para demostrar los beneficios de la API Java 8 Stream.
SME_Dev

Recibo un error cuando trato de asignar este valor -> Ejemplo: ArrayList <String> total total = (ArrayList <String>) intersección (list2, list1) ---> no puede emitir java.util.arraylist a java.util.arraylist < string>
entrega

123

La colección (también ArrayList) tiene:

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

Use una implementación de Lista si acepta repeticiones, una Implementación de conjunto si no:

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
Se ha sugerido una edición de que esta unión "es incorrecta ya que contendrá elementos comunes dos veces" . La edición recomendó utilizar a HashSeten su lugar.
Kos

55
En realidad fue editado, vea: "Use una implementación de Lista si acepta repeticiones, una Implementación de conjunto si no:"
lukastymo

77
No, retener todo no es intersección para la lista. En lo anterior, todos los elementos en col que no están en otherCol se eliminan. Digamos que otherCol es {a, b, b, c} y col es {b, b, b, c, d}. Entonces col termina con {b, b, b, c} que no es estrictamente la intersección de los dos. Espero que sea {b, b, c}. Se está realizando una operación diferente.
demongolem

1
Tampoco veo cómo addAll()es la unión para las listas; solo concatena la segunda lista al final de la primera. Una operación de unión evitaría agregar un elemento si la primera lista ya lo contiene.
dimo414

66

Esta publicación es bastante antigua, pero sin embargo fue la primera que apareció en Google al buscar ese tema.

Quiero dar una actualización usando flujos Java 8 haciendo (básicamente) lo mismo en una sola línea:

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 alguien tiene una solución mejor / más rápida, avíseme, pero esta solución es un buen revestimiento que se puede incluir fácilmente en un método sin agregar una clase / método auxiliar innecesario y aún así mantener la legibilidad.


19
Ooof, puede ser una buena frase pero lleva O (n ^ 2) tiempo. Convierta una de las listas a y Setluego use el containsmétodo del conjunto . No todo en la vida tiene que hacerse con corrientes.
dimo414

31
list1.retainAll(list2) - is intersection

la unión será removeAlly luego addAll.

Encuentre más en la documentación de la colección (ArrayList es una colección) http://download.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html


1
Ambas retainAll()y removeAll()son operaciones O (n ^ 2) en listas. Podemos hacerlo mejor.
dimo414

1
He votado pero ahora tengo una pregunta. retainAllde {1, 2, 2, 3, 4, 5} sobre {1, 2, 3} da como resultado {1, 2, 2, 3}. ¿No debería ser {1, 2, 3} ser la intersección?
GyuHyeon Choi

21

Uniones e intersecciones definidas solo para conjuntos, no para listas. Como lo mencionaste.

Verifique la biblioteca de guayaba para ver los filtros. También la guayaba proporciona intersecciones y sindicatos reales.

 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

Puedes usar CollectionUtilsdesde apache commons .


77
En caso de que alguien encuentre esta respuesta demasiado breve: 'CollectionUtils.containsAny' y 'CollectionUtils.containsAll' son los métodos.
Sebastián

2
es extraño que CollectionUtils de apache commons no admita genéricos
Vasyl Sarzhynskyi

7

La solución marcada no es eficiente. Tiene una complejidad de tiempo O (n ^ 2). Lo que podemos hacer es ordenar ambas listas y ejecutar un algoritmo de intersección como el siguiente.

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

Este tiene una complejidad de O (n log n + n) que está en O (n log n). La unión se realiza de manera similar. Solo asegúrese de realizar las modificaciones adecuadas en las declaraciones if-elseif-else.

También puede usar iteradores si lo desea (sé que son más eficientes en C ++, no sé si esto también es cierto en Java).


1
No es lo suficientemente genérico, T puede no ser comparable y, en algunos casos, comparar es caro ...
Boris Churzin

No genérico, estoy totalmente de acuerdo. La comparación es cara? ¿Cómo resolverías eso?
AJed

Lamentablemente, sería más barato hacerlo en O (n ^ 2) :) Para Numbers, esta solución es buena ...
Boris Churzin

Lamentablemente, no respondiste mi pregunta. Permítanme reformularlo, ¿cómo es mejor O (n ^ 2) dada una función de comparación de costo c (n)?
AJed

1
Convertir una entrada en un conjunto y llamar contains()en un bucle (como sugiere Devenv) llevaría tiempo O (n + m). La clasificación es innecesariamente complicada y lleva tiempo O (n log n + m log n + n). De acuerdo, eso reduce el tiempo de O (n log n), pero aún es peor que el tiempo lineal, y mucho más complejo.
dimo414

4

Creo que debería usar a Setpara guardar los archivos si desea hacer intersección y unión en ellos. A continuación, puede utilizar la guayaba 's conjuntos de clase para hacer union, intersectiony filtrar por una Predicateasí. La diferencia entre estos métodos y las otras sugerencias es que todos estos métodos crean vistas perezosas de la unión, intersección, etc. de los dos conjuntos. Apache Commons crea una nueva colección y le copia datos. retainAllcambia una de tus colecciones eliminando elementos de ella.


4

Aquí hay una manera de cómo puedes hacer una intersección con las transmisiones (recuerda que debes usar Java 8 para las transmisiones):

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 ejemplo para listas con diferentes tipos. Si tiene una relación entre foo y bar y puede obtener un objeto de barra de foo que puede modificar su transmisión:

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
  • RetenAll modificará su lista
  • Guava no tiene API para List (solo para set)

Encontré ListUtils muy útil para este caso de uso.

Utilice ListUtils de org.apache.commons.collections si no desea modificar la lista existente.

ListUtils.intersection(list1, list2)


3

Puede usar 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

En Java 8, uso métodos de ayuda simples como este:

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 los objetos de la lista son hashable (es decir, tienen un hashCode decente y una función igual), el enfoque más rápido entre tablas aprox. size> 20 es construir un HashSet para la mayor de las dos listas.

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

También estaba trabajando en una situación similar y llegué aquí buscando ayuda. Terminé encontrando mi propia solución para matrices. ArrayList AbsentDates = new ArrayList (); // Almacenará Array1-Array2

Nota: Publicar esto si puede ayudar a alguien que llega a esta página para obtener ayuda.

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

Intersección de dos listas de diferentes objetos basados ​​en clave común - 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());
    }

¿Qué tal la diferencia establecida entre esos 2 lista?
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 + (Probablemente el mejor rendimiento)

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 no le importa el rendimiento y prefiere un código más pequeño, simplemente use:

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

0

Solución final:

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

Primero, estoy copiando todos los valores de las matrices en una sola matriz, luego estoy eliminando valores duplicados en la matriz. Línea 12, explicando si el mismo número ocurre más de tiempo, luego coloque algún valor de basura adicional en la posición "j". Al final, atraviese desde el inicio hasta el final y verifique si se produce el mismo valor de basura, luego deséchelo.

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
¡Bienvenido a Stack Overflow! Tenga en cuenta que la pregunta es sobre ArrayList. Además, me temo que esta implementación en particular deja cosas que desear. El valor 99999999, que se usa como centinela, puede aparecer en la entrada. Sería mejor usar una estructura dinámica, como ArrayList, para almacenar el resultado de la unión.
SL Barth - Restablecer Monica

1
Explica el código que has presentado en lugar de solo una respuesta de código.
tmarois

Solo estoy dando una pista de que tienes que poner cualquier valor de basura
Ashutosh

Me alegra ver que agregaste una explicación. Lamentablemente, la respuesta en sí sigue siendo mala. No hay razón para usar matrices. Debe usar una estructura dinámica como ArrayList. Si (por alguna razón) debe usar matrices, debe considerar usar una matriz en Integerlugar de int. Luego puede usar en nulllugar de su "valor de basura". Los "valores basura" o los "valores centinela" suelen ser una mala idea, ya que estos valores aún pueden aparecer en la entrada.
SL Barth - Restablece a Monica el

0

Después de la prueba, aquí está mi mejor enfoque de intersección.

Velocidad más rápida en comparación con el enfoque puro de HashSet. HashSet y HashMap a continuación tienen un rendimiento similar para matrices con más de 1 millón de registros.

En cuanto al enfoque de Java 8 Stream, la velocidad es bastante lenta para un tamaño de matriz mayor de 10k.

Espero que esto pueda ayudar.

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

Retención de todo el método () para encontrar elementos comunes ... es decir, intersección list1.retainAll (list2)


-1

Si tuviera sus datos en Conjuntos, podría usar la Setsclase Guava .


-1

Si el número coincide con el que estoy comprobando, se produce por primera vez o no con la ayuda de "indexOf ()" si el número coincide por primera vez, luego imprime y guarda en una cadena para que la próxima vez que coincida el mismo número se gane ' t imprimir porque debido a la condición "indexOf ()" será 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
No solo publique el código como respuesta, dé una pequeña explicación de lo que está haciendo
Brandon Zamudio

es mi primer programa que cargué
Ashutosh

2
Aunque este código puede ayudar a resolver el problema, no explica por qué y / o cómo responde la pregunta. Proporcionar este contexto adicional mejoraría significativamente su valor a largo plazo. Por favor, editar su respuesta para agregar explicación, incluyendo lo que se aplican limitaciones y supuestos.
Toby Speight
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.