objetos - metodo compareto java
Compara dos colecciones Java usando Comparator en lugar de iguales() (4)
Planteamiento del problema
Tengo dos colecciones del mismo tipo de objeto que quiero comparar. En este caso, quiero compararlos en función de un atributo que no tenga en cuenta en equals()
para los Objetos. En mi ejemplo, estoy usando colecciones clasificadas de nombres, por ejemplo:
public class Name {
private String name;
private int weightedRank;
//getters & setters
@Override
public boolean equals(Object obj) {
return this.name.equals(obj.name); //Naive implementation just to show
//equals is based on the name field.
}
}
Quiero comparar las dos Colecciones para afirmar que, para la posición i
en cada Colección, el weightedRank
de cada Nombre en esa posición es el mismo valor. Hice algo de Google pero no encontré un método adecuado en Commons Collections ni en ninguna otra API, por lo que se me ocurrió lo siguiente:
public <T> boolean comparatorEquals(Collection<T> col1, Collection<T> col2,
Comparator<T> c)
{
if (col1 == null)
return col2 == null;
if (col2 == null)
return false;
if (col1.size() != col2.size())
return false;
Iterator<T> i1 = col1.iterator(), i2 = col2.iterator();
while(i1.hasNext() && i2.hasNext()) {
if (c.compare(i1.next(), i2.next()) != 0) {
return false;
}
}
return true;
}
Pregunta
Hay otra manera de hacer esto? ¿Me perdí un método obvio de Commons Collections?
Relacionado
También descubrí esta pregunta en SO, que es similar, aunque en ese caso creo que reemplazar a equals()
tiene un poco más de sentido.
Editar
Algo muy similar a esto será el lanzamiento de Apache Commons Collections en un futuro cercano (en el momento de escribir este artículo). Consulte https://issues.apache.org/jira/browse/COLLECTIONS-446 .
No estoy seguro de que esta manera sea realmente mejor, pero es "otra manera" ...
Tome sus dos colecciones originales y cree otras nuevas que contengan un adaptador para cada objeto base. El adaptador debe tener .equals()
y .hashCode()
basados en Name.calculateWeightedRank()
. Luego puede usar la igualdad de colección normal para comparar las colecciones de adaptadores.
* Editar *
Usando el hashCode / equals generation estándar de Eclipse para el Adapter
. Su código solo llamará adaptCollection en cada una de sus colecciones base, luego List.equals () los dos resultados.
public class Adapter {
public List<Adapter> adaptCollection(List<Name> names) {
List<Adapter> adapters = new ArrayList<Adapter>(names.size());
for (Name name : names) {
adapters.add(new Adapter(name));
}
return adapters;
}
private final int name;
public Adapter(Name name) {
this.name = name.getWeightedResult();
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + name;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Adapter other = (Adapter) obj;
if (name != other.name)
return false;
return true;
}
}
Podría usar la clase de Equivalence guayaba para desacoplar las nociones de "comparación" y "equivalencia". Aún tendría que escribir su método de comparación (AFAIK Guava no lo tiene) que acepte una subclase de equivalencia en lugar del comparador, pero al menos su código sería menos confuso, y podría comparar sus colecciones según los criterios de equivalencia.
El uso de una colección de objetos envueltos en equivalencia (consulte el método de ajuste en Equivalencia ) sería similar a la solución basada en el Adaptador propuesta por sharakan, pero la implementación de la equivalencia se desacoplaría de la implementación del adaptador, lo que le permite utilizar fácilmente múltiples criterios de Equivalencia.
Puede usar el nuevo método isEqualCollection
agregado a CollectionUtils
desde la versión 4. Este método utiliza un mecanismo de comparsion externo provisto por la implementación de la interfaz de Equator
. Por favor, compruebe este javadocs: CollectionUtils.isEqualCollection(...) y Equator .
EDITAR : Se eliminó la respuesta anterior.
Otra opción que tiene es crear una interfaz llamada Weighted
que podría tener este aspecto:
public interface Weighted {
int getWeightedRank();
}
Luego, pida a su clase de Name
implemente esta interfaz. Entonces podrías cambiar tu método para lucir así:
public <T extends Weighted> boolean weightedEquals(Collection<T> col1, Collection<T> col2)
{
if (col1 == null)
return col2 == null;
if (col2 == null)
return false;
if (col1.size() != col2.size())
return false;
Iterator<T> i1 = col1.iterator(), i2 = col2.iterator();
while(i1.hasNext() && i2.hasNext()) {
if (i1.next().getWeightedRank() != i2.next().getWeightedRank()) {
return false;
}
}
return true;
}
Luego, a medida que encuentre clases adicionales que deben ser ponderadas y comparadas, puede incluirlas en su colección y también se pueden comparar entre sí. Solo una idea.