una listas lista español ejemplos crear como colecciones java collections

listas - ¿Cuál es la mejor manera de comparar dos colecciones en Java y actuar sobre ellas?



list java español (8)

Tengo dos colecciones del mismo objeto, Collection<Foo> oldSet y Collection<Foo> newSet . La lógica requerida es la siguiente:

  • si foo está en (*) oldSet pero no newSet , llama a doRemove(foo)
  • de lo contrario, si foo no está en oldSet pero en newSet , llame a doAdd(foo)
  • De lo contrario, si foo está en ambas colecciones pero modificado, llama a doUpdate(oldFoo, newFoo)
  • else if !foo.activated && foo.startDate >= now , llame a doStart(foo)
  • else if foo.activated && foo.endDate <= now , llame a doEnd(foo)

(*) "in" significa que el identificador único coincide, no necesariamente el contenido.

El código actual (heredado) hace muchas comparaciones para descubrir addSet , updateSet , startSet , startSet y endSet , y luego endSet para actuar sobre cada elemento.

El código es bastante desordenado (en parte porque ya he omitido algo de lógica de spaghetti) y estoy tratando de refactorizarlo. Algo más de información de fondo:

  • Por lo que sé, el oldSet y newSet están respaldados por ArrayList
  • Cada conjunto contiene menos de 100 elementos, lo más probable es que tenga un máximo de 20
  • Este código se llama con frecuencia (medido en millones / día), aunque los conjuntos rara vez difieren

Mis preguntas:

  • Si convierto a oldSet y newSet en HashMap<Foo> (el orden no es relevante aquí), con los ID como claves, ¿haría el código más fácil de leer y más fácil de comparar? ¿Cuánto de tiempo y rendimiento de memoria es pérdida en la conversión?
  • ¿Sería más eficiente y conciso iterar los dos conjuntos y realizar la operación apropiada?

Para un conjunto tan pequeño, en general, no vale la pena convertirlo de un array a un HashMap / set. De hecho, es mejor que los guardes en una matriz y luego los clasifiques por clave e iteremos sobre ambas listas simultáneamente para hacer la comparación.


Me movería a las listas y lo resolvería de esta manera:

  1. Ordene ambas listas por ID ascendente usando Comparator personalizado si los objetos en las listas no son Comparables
  2. Itere sobre los elementos en ambas listas, como en la fase de fusión en el algoritmo de ordenación por fusión , pero en lugar de combinar listas, verificas tu lógica.

El código sería más o menos así:

/* Main method */ private void execute(Collection<Foo> oldSet, Collection<Foo> newSet) { List<Foo> oldList = asSortedList(oldSet); List<Foo> newList = asSortedList(newSet); int oldIndex = 0; int newIndex = 0; // Iterate over both collections but not always in the same pace while( oldIndex < oldList.size() && newIndex < newIndex.size()) { Foo oldObject = oldList.get(oldIndex); Foo newObject = newList.get(newIndex); // Your logic here if(oldObject.getId() < newObject.getId()) { doRemove(oldObject); oldIndex++; } else if( oldObject.getId() > newObject.getId() ) { doAdd(newObject); newIndex++; } else if( oldObject.getId() == newObject.getId() && isModified(oldObject, newObject) ) { doUpdate(oldObject, newObject); oldIndex++; newIndex++; } else { ... } }// while // Check if there are any objects left in *oldList* or *newList* for(; oldIndex < oldList.size(); oldIndex++ ) { doRemove( oldList.get(oldIndex) ); }// for( oldIndex ) for(; newIndex < newList.size(); newIndex++ ) { doAdd( newList.get(newIndex) ); }// for( newIndex ) }// execute( oldSet, newSet ) /** Create sorted list from collection If you actually perform any actions on input collections than you should always return new instance of list to keep algorithm simple. */ private List<Foo> asSortedList(Collection<Foo> data) { List<Foo> resultList; if(data instanceof List) { resultList = (List<Foo>)data; } else { resultList = new ArrayList<Foo>(data); } Collections.sort(resultList) return resultList; }


Creo que la forma más fácil de hacerlo es mediante el uso de las colecciones de apache api - CollectionUtils.subtract (list1, list2) siempre que las listas sean del mismo tipo.


Para comparar una lista o conjunto, podemos usar Arrays.equals(object[], object[]) . Verificará solo los valores. Para obtener el Object[] podemos usar el método Collection.toArray() .


Puede usar flujos Java 8, por ejemplo

set1.stream().filter(s -> set2.contains(s)).collect(Collectors.toSet());

o Establece una clase de Guava :

Set<String> intersection = Sets.intersection(set1, set2); Set<String> difference = Sets.difference(set1, set2); Set<String> symmetricDifference = Sets.symmetricDifference(set1, set2); Set<String> union = Sets.union(set1, set2);


He creado una aproximación de lo que creo que estás buscando simplemente usando el Framework Collections en Java. Francamente, creo que probablemente sea excesivo como lo señala @Mike Deck. Para un conjunto tan pequeño de elementos para comparar y procesar, creo que los arreglos serían una mejor opción desde un punto de vista de procedimiento, pero aquí está mi solución pseudo-codificada (porque soy floja). Supongo que la clase Foo es comparable en función de su id. Única y no de todos los datos contenidos en ella:

Collection<Foo> oldSet = ...; Collection<Foo> newSet = ...; private Collection difference(Collection a, Collection b) { Collection result = a.clone(); result.removeAll(b) return result; } private Collection intersection(Collection a, Collection b) { Collection result = a.clone(); result.retainAll(b) return result; } public doWork() { // if foo is in(*) oldSet but not newSet, call doRemove(foo) Collection removed = difference(oldSet, newSet); if (!removed.isEmpty()) { loop removed { Foo foo = removedIter.next(); doRemove(foo); } } //else if foo is not in oldSet but in newSet, call doAdd(foo) Collection added = difference(newSet, oldSet); if (!added.isEmpty()) { loop added { Foo foo = addedIter.next(); doAdd(foo); } } // else if foo is in both collections but modified, call doUpdate(oldFoo, newFoo) Collection matched = intersection(oldSet, newSet); Comparator comp = new Comparator() { int compare(Object o1, Object o2) { Foo f1, f2; if (o1 instanceof Foo) f1 = (Foo)o1; if (o2 instanceof Foo) f2 = (Foo)o2; return f1.activated == f2.activated ? f1.startdate.compareTo(f2.startdate) == 0 ? ... : f1.startdate.compareTo(f2.startdate) : f1.activated ? 1 : 0; } boolean equals(Object o) { // equal to this Comparator..not used } } loop matched { Foo foo = matchedIter.next(); Foo oldFoo = oldSet.get(foo); Foo newFoo = newSet.get(foo); if (comp.compareTo(oldFoo, newFoo ) != 0) { doUpdate(oldFoo, newFoo); } else { //else if !foo.activated && foo.startDate >= now, call doStart(foo) if (!foo.activated && foo.startDate >= now) doStart(foo); // else if foo.activated && foo.endDate <= now, call doEnd(foo) if (foo.activated && foo.endDate <= now) doEnd(foo); } } }

En cuanto a sus preguntas: Si convierto a oldSet y newSet en HashMap (aquí no importa el orden), con las identificaciones como claves, ¿haría el código más fácil de leer y más fácil de comparar? ¿Cuánto de tiempo y rendimiento de memoria es pérdida en la conversión? Creo que probablemente harías el código más legible usando un Mapa PERO ... probablemente usarías más memoria y tiempo durante la conversión.

¿Sería más eficiente y conciso iterar los dos conjuntos y realizar la operación apropiada? Sí, esto sería lo mejor de ambos mundos, especialmente si sigues los consejos de @Mike Sharek de rodar tu propia lista con los métodos especializados o si sigues algo parecido al patrón de diseño de visitante para recorrer tu colección y procesar cada elemento.


La biblioteca commons.collections de Apache tiene una clase CollectionUtils que proporciona métodos fáciles de usar para la manipulación / comprobación de colecciones, como la intersección, la diferencia y la unión.

Los documentos de la API org.apache.commons.collections.CollectionUtils están aquí .


public static boolean doCollectionsContainSameElements( Collection<Integer> c1, Collection<Integer> c2){ if (c1 == null || c2 == null) { return false; } else if (c1.size() != c2.size()) { return false; } else { return c1.containsAll(c2) && c2.containsAll(c1); } }