sirve - metodo equals string java
Cómo comparar objetos por múltiples campos (20)
(de House of Code )
Desordenado y enrevesado: clasificación a mano
Collections.sort(pizzas, new Comparator<Pizza>() {
@Override
public int compare(Pizza p1, Pizza p2) {
int sizeCmp = p1.size.compareTo(p2.size);
if (sizeCmp != 0) {
return sizeCmp;
}
int nrOfToppingsCmp = p1.nrOfToppings.compareTo(p2.nrOfToppings);
if (nrOfToppingsCmp != 0) {
return nrOfToppingsCmp;
}
return p1.name.compareTo(p2.name);
}
});
Esto requiere mucho tipeo, mantenimiento y es propenso a errores.
La forma reflexiva: Ordenando con BeanComparator
ComparatorChain chain = new ComparatorChain(Arrays.asList(
new BeanComparator("size"),
new BeanComparator("nrOfToppings"),
new BeanComparator("name")));
Collections.sort(pizzas, chain);
Obviamente, esto es más conciso, pero aún más propenso a errores, ya que pierde su referencia directa a los campos mediante el uso de cadenas en su lugar. Ahora, si se cambia el nombre de un campo, el compilador ni siquiera informará un problema. Además, debido a que esta solución usa reflexión, la clasificación es mucho más lenta.
Cómo llegar: clasificación con Google Guava''s ComparisonChain
Collections.sort(pizzas, new Comparator<Pizza>() {
@Override
public int compare(Pizza p1, Pizza p2) {
return ComparisonChain.start().compare(p1.size, p2.size).compare(p1.nrOfToppings, p2.nrOfToppings).compare(p1.name, p2.name).result();
// or in case the fields can be null:
/*
return ComparisonChain.start()
.compare(p1.size, p2.size, Ordering.natural().nullsLast())
.compare(p1.nrOfToppings, p2.nrOfToppings, Ordering.natural().nullsLast())
.compare(p1.name, p2.name, Ordering.natural().nullsLast())
.result();
*/
}
});
Esto es mucho mejor, pero requiere un código de placa de caldera para el caso de uso más común: los valores nulos deben valorarse menos de manera predeterminada. Para null-fields, debe proporcionar una directiva adicional a Guava qué hacer en ese caso. Este es un mecanismo flexible si desea hacer algo específico, pero a menudo desea el caso predeterminado (es decir, 1, a, b, z, nulo).
Ordenando con Apache Commons CompareToBuilder
Collections.sort(pizzas, new Comparator<Pizza>() {
@Override
public int compare(Pizza p1, Pizza p2) {
return new CompareToBuilder().append(p1.size, p2.size).append(p1.nrOfToppings, p2.nrOfToppings).append(p1.name, p2.name).toComparison();
}
});
Como Guava''s ComparisonChain, esta clase de biblioteca se ordena fácilmente en múltiples campos, pero también define el comportamiento predeterminado para valores nulos (es decir, 1, a, b, z, nulo). Sin embargo, tampoco puede especificar nada más, a menos que proporcione su propio Comparador.
Así
En última instancia todo se reduce al sabor y la necesidad de flexibilidad (Guava''s ComparisonChain) versus código conciso (CompareToBuilder de Apache).
Método de bonificación
Encontré una buena solución que combina múltiples comparadores por orden de prioridad en CodeReview en un MultiComparator
:
class MultiComparator<T> implements Comparator<T> {
private final List<Comparator<T>> comparators;
public MultiComparator(List<Comparator<? super T>> comparators) {
this.comparators = comparators;
}
public MultiComparator(Comparator<? super T>... comparators) {
this(Arrays.asList(comparators));
}
public int compare(T o1, T o2) {
for (Comparator<T> c : comparators) {
int result = c.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}
public static <T> void sort(List<T> list, Comparator<? super T>... comparators) {
Collections.sort(list, new MultiComparator<T>(comparators));
}
}
Por supuesto, Apache Commons Collections ya tiene una utilidad para esto:
ComparatorUtils.chainedComparator(comparatorCollection)
Collections.sort(list, ComparatorUtils.chainedComparator(comparators));
Supongamos que tiene algunos objetos que tienen varios campos que pueden ser comparados por:
public class Person {
private String firstName;
private String lastName;
private String age;
/* Constructors */
/* Methods */
}
Entonces en este ejemplo, cuando preguntas si:
a.compareTo(b) > 0
usted podría estar preguntando si el apellido de a viene antes de b, o si a es más viejo que b, etc.
¿Cuál es la forma más limpia de permitir la comparación múltiple entre este tipo de objetos sin agregar desorden innecesario o sobrecarga?
-
java.lang.Comparable
interfazjava.lang.Comparable
permite la comparación solo por un campo - Agregar numerosos métodos de comparación (es decir,
compareByFirstName()
,compareByAge()
, etc ...) está desordenado en mi opinión.
Entonces, ¿cuál es la mejor manera de hacerlo?
@Patrick Para ordenar más de un campo consecutivamente, pruebe ComparatorChain
Un ComparatorChain es un Comparador que envuelve uno o más Comparadores en secuencia. ComparatorChain llama a cada Comparador en secuencia hasta que 1) cualquier Comparador individual devuelve un resultado distinto de cero (y luego se devuelve ese resultado), o 2) Se agota el ComparatorChain (y se devuelve cero). Este tipo de ordenamiento es muy similar a la clasificación de múltiples columnas en SQL, y esta clase permite que las clases Java emulen ese tipo de comportamiento al ordenar una Lista.
Para facilitar aún más la clasificación tipo SQL, el orden de cualquier Comparador individual en la lista puede ser revertido.
Llamar a un método que agrega nuevos Comparadores o cambia el orden ascendente / descendente después de comparar (Objeto, Objeto) ha sido llamado dará como resultado una excepción de operación no admitida. Sin embargo, tenga cuidado de no alterar la Lista de comparadores subyacente o el BitSet que define el orden de clasificación.
Las instancias de ComparatorChain no están sincronizadas. La clase no es segura para subprocesos en el momento de la construcción, pero es segura para la ejecución de subprocesos después de que se completan todas las operaciones de configuración.
A partir de la respuesta de Steve, el operador ternario puede usarse:
public int compareTo(Person other) {
int f = firstName.compareTo(other.firstName);
int l = lastName.compareTo(other.lastName);
return f != 0 ? f : l != 0 ? l : Integer.compare(age, other.age);
}
Con Java 8:
Comparator.comparing((Person p)->p.firstName)
.thenComparing(p->p.lastName)
.thenComparingInt(p->p.age);
Si tiene métodos de acceso:
Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge);
Si una clase implementa Comparable, dicho comparador puede usarse en el método compareTo:
@Override
public int compareTo(Person o){
return Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge)
.compare(this, o);
}
Creo que sería más confuso si tu algoritmo de comparación fuera "inteligente". Me gustaría ir con los numerosos métodos de comparación que sugirió.
La única excepción para mí sería la igualdad. Para las pruebas unitarias, me ha sido útil anular los .Equals (en .net) para determinar si varios campos son iguales entre dos objetos (y no que las referencias sean iguales).
Debería implementar Comparable <Person>
. Suponiendo que todos los campos no serán nulos (por razones de simplicidad), esa edad es un int, y la clasificación de comparación es first, last, age, el método compareTo
es bastante simple:
public int compareTo(Person other) {
int i = firstName.compareTo(other.firstName);
if (i != 0) return i;
i = lastName.compareTo(other.lastName);
if (i != 0) return i;
return Integer.compare(age, other.age);
}
El siguiente blog muestra un buen ejemplo comparativo encadenado
http://www.codejava.net/java-core/collections/sorting-a-list-by-multiple-attributes-example
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* This is a chained comparator that is used to sort a list by multiple
* attributes by chaining a sequence of comparators of individual fields
* together.
*
*/
public class EmployeeChainedComparator implements Comparator<Employee> {
private List<Comparator<Employee>> listComparators;
@SafeVarargs
public EmployeeChainedComparator(Comparator<Employee>... comparators) {
this.listComparators = Arrays.asList(comparators);
}
@Override
public int compare(Employee emp1, Employee emp2) {
for (Comparator<Employee> comparator : listComparators) {
int result = comparator.compare(emp1, emp2);
if (result != 0) {
return result;
}
}
return 0;
}
}
Comparador de llamadas:
Collections.sort(listEmployees, new EmployeeChainedComparator(
new EmployeeJobTitleComparator(),
new EmployeeAgeComparator(),
new EmployeeSalaryComparator())
);
En lugar de métodos de comparación, es posible que desee definir varios tipos de subclases "Comparador" dentro de la clase Persona. De esta forma, puede pasarlos a los métodos de clasificación de colecciones estándar.
Es fácil comparar dos objetos con el método hashcode en java`
public class Sample{
String a=null;
String b=null;
public Sample(){
a="s";
b="a";
}
public Sample(String a,String b){
this.a=a;
this.b=b;
}
public static void main(String args[]){
Sample f=new Sample("b","12");
Sample s=new Sample("b","12");
//will return true
System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));
//will return false
Sample f=new Sample("b","12");
Sample s=new Sample("b","13");
System.out.println((s.a.hashCode()+s.b.hashCode())==(f.a.hashCode()+f.b.hashCode()));
}
Es fácil de hacer usando la biblioteca de guayaba de Google .
por ejemplo, Objects.equal(name, name2) && Objects.equal(age, age2) && ...
Más ejemplos:
- https://.com/a/5039178/1180621
Escribir un Comparator
manualmente para un caso de uso así es una solución terrible IMO. Tales enfoques ad hoc tienen muchos inconvenientes:
- Sin código reutilizado Viola SECO.
- Repetitivo.
- Mayor posibilidad de errores.
Entonces, ¿cuál es la solución?
Primero algo de teoría.
Denotemos la proposición "tipo A
apoya la comparación" por Ord A
(Desde la perspectiva del programa, puede pensar en Ord A
como un objeto que contiene lógica para comparar dos A
s. Sí, al igual que Comparator
).
Ahora bien, si Ord A
y Ord B
, entonces su compuesto (A, B)
también debería ser compatible con la comparación. es decir, Ord (A, B)
. Si Ord A
, Ord B
y Ord C
, entonces Ord (A, B, C)
.
Podemos extender este argumento a arity arbitrario, y decir:
Ord A, Ord B, Ord C, ..., Ord Z
⇒ Ord (A, B, C, .., Z)
Llamemos a esta declaración 1.
La comparación de los compuestos funcionará tal como lo describió en su pregunta: la primera comparación se probará primero, luego la siguiente, luego la siguiente, y así sucesivamente.
Esa es la primera parte de nuestra solución. Ahora la segunda parte.
Si sabes que Ord A
, y sabes cómo transformar B
en A
(llama a esa función de transformación f
), entonces también puedes tener Ord B
¿Cómo? Bueno, cuando se comparen las dos instancias B
, primero las transformará en A
usando f
y luego aplicará Ord A
Aquí, estamos mapeando la transformación B → A
a Ord A → Ord B
Esto se conoce como mapeo contravariante (o comap
para abreviar).
Ord A, (B → A)
⇒ comap Ord B
Llamemos a esta declaración 2.
Ahora apliquemos esto a tu ejemplo.
Tiene un tipo de datos llamado Person
que comprende tres campos de tipo String
.
Sabemos que
Ord String
. Por la instrucción 1,Ord (String, String, String)
.Podemos escribir fácilmente una función de
Person
a(String, String, String)
. (Simplemente devuelva los tres campos). Como conocemosOrd (String, String, String)
yPerson → (String, String, String)
, según el enunciado 2, podemos usarcomap
para obtenerOrd Person
.
QED.
¿Cómo implemento todos estos conceptos?
La buena noticia es que no es necesario. Ya existe una biblioteca que implementa todas las ideas descritas en esta publicación. (Si tiene curiosidad de cómo se implementan estos, puede mirar debajo del capó ).
Así es como se verá el código con él:
Ord<Person> personOrd =
p3Ord(stringOrd, stringOrd, stringOrd).comap(
new F<Person, P3<String, String, String>>() {
public P3<String, String, String> f(Person x) {
return p(x.getFirstName(), x.getLastname(), x.getAge());
}
}
);
Explicación:
-
stringOrd
es un objeto de tipoOrd<String>
. Esto corresponde a nuestra propuesta original de "compatibilidad de comparación". -
p3Ord
es un método que tomaOrd<A>
,Ord<B>
,Ord<C>
, y devuelveOrd<P3<A, B, C>>
. Esto corresponde al enunciado 1. (P3
significa producto con tres elementos. Producto es un término algebraico para compuestos). -
comap
corresponde a bien,comap
. -
F<A, B>
representa una función de transformaciónA → B
-
p
es un método de fábrica para crear productos. - La expresión completa corresponde a la declaración 2.
Espero que ayude.
Otra opción que siempre puedes considerar es Apache Commons. Proporciona muchas opciones.
import org.apache.commons.lang3.builder.CompareToBuilder;
Ex:
public int compare(Person a, Person b){
return new CompareToBuilder()
.append(a.getName(), b.getName())
.append(a.getAddress(), b.getAddress())
.toComparison();
}
Para aquellos que pueden usar la API de streaming Java 8, hay un enfoque más ordenado que está bien documentado aquí: Lambdas y clasificación
Estaba buscando el equivalente de C # LINQ:
.ThenBy(...)
Encontré el mecanismo en Java 8 en el Comparador:
.thenComparing(...)
Así que aquí está el fragmento que demuestra el algoritmo.
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
Consulte el enlace anterior para obtener una explicación más clara sobre cómo la inferencia de tipos de Java hace que sea un poco más complicado de definir en comparación con LINQ.
Aquí está la prueba unitaria completa para referencia:
@Test
public void testChainedSorting()
{
// Create the collection of people:
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Dan", 4));
people.add(new Person("Andi", 2));
people.add(new Person("Bob", 42));
people.add(new Person("Debby", 3));
people.add(new Person("Bob", 72));
people.add(new Person("Barry", 20));
people.add(new Person("Cathy", 40));
people.add(new Person("Bob", 40));
people.add(new Person("Barry", 50));
// Define chained comparators:
// Great article explaining this and how to make it even neater:
// http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
// Sort the stream:
Stream<Person> personStream = people.stream().sorted(comparator);
// Make sure that the output is as expected:
List<Person> sortedPeople = personStream.collect(Collectors.toList());
Assert.assertEquals("Andi", sortedPeople.get(0).name); Assert.assertEquals(2, sortedPeople.get(0).age);
Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
Assert.assertEquals("Bob", sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
Assert.assertEquals("Bob", sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
Assert.assertEquals("Bob", sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
Assert.assertEquals("Dan", sortedPeople.get(7).name); Assert.assertEquals(4, sortedPeople.get(7).age);
Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3, sortedPeople.get(8).age);
// Andi : 2
// Barry : 20
// Barry : 50
// Bob : 40
// Bob : 42
// Bob : 72
// Cathy : 40
// Dan : 4
// Debby : 3
}
/**
* A person in our system.
*/
public static class Person
{
/**
* Creates a new person.
* @param name The name of the person.
* @param age The age of the person.
*/
public Person(String name, int age)
{
this.age = age;
this.name = name;
}
/**
* The name of the person.
*/
public String name;
/**
* The age of the person.
*/
public int age;
@Override
public String toString()
{
if (name == null) return super.toString();
else return String.format("%s : %d", this.name, this.age);
}
}
Puede escribir una clase de comparación que compare dos objetos Persona, y puede examinar tantos campos como desee. Puede poner una variable en su comparador que le diga a qué campo comparar, aunque probablemente sería más simple escribir múltiples comparadores.
Si hay varias formas en que un usuario puede pedir una persona, también puede tener la configuración de múltiples Comparator como constantes en alguna parte. La mayoría de las operaciones de ordenación y colecciones ordenadas toman un comparador como parámetro.
Si implementa la interfaz Comparable , querrá elegir una propiedad simple para ordenar. Esto se conoce como ordenamiento natural. Piense en esto como el predeterminado. Siempre se usa cuando no se proporciona un comparador específico. Por lo general, este es el nombre, pero su caso de uso puede requerir algo diferente. Usted es libre de usar cualquier cantidad de otros Comparadores que pueda suministrar a varias API de colecciones para anular el orden natural.
También tenga en cuenta que, por lo general, si a.compareTo (b) == 0, entonces a.equals (b) == verdadero. Está bien si no, pero hay efectos secundarios a tener en cuenta. Vea los excelentes javadocs en la interfaz Comparable y encontrará mucha información excelente sobre esto.
También puedes echar un vistazo a Enum que implementa Comparator.
http://tobega.blogspot.com/2008/05/beautiful-enums.html
p.ej
Collections.sort(myChildren, Child.Order.ByAge.descending());
//Following is the example in jdk 1.8
package com;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
class User {
private String firstName;
private String lastName;
private Integer age;
public Integer getAge() {
return age;
}
public User setAge(Integer age) {
this.age = age;
return this;
}
public String getFirstName() {
return firstName;
}
public User setFirstName(String firstName) {
this.firstName = firstName;
return this;
}
public String getLastName() {
return lastName;
}
public User setLastName(String lastName) {
this.lastName = lastName;
return this;
}
}
public class MultiFieldsComparision {
public static void main(String[] args) {
List<User> users = new ArrayList<User>();
User u1 = new User().setFirstName("Pawan").setLastName("Singh").setAge(38);
User u2 = new User().setFirstName("Pawan").setLastName("Payal").setAge(37);
User u3 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(60);
User u4 = new User().setFirstName("Anuj").setLastName("Kumar").setAge(43);
User u5 = new User().setFirstName("Pawan").setLastName("Chamoli").setAge(44);
User u6 = new User().setFirstName("Pawan").setLastName("Singh").setAge(5);
users.add(u1);
users.add(u2);
users.add(u3);
users.add(u4);
users.add(u5);
users.add(u6);
System.out.println("****** Before Sorting ******");
users.forEach(user -> {
System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
});
System.out.println("****** Aftre Sorting ******");
users.sort(
Comparator.comparing(User::getFirstName).thenComparing(User::getLastName).thenComparing(User::getAge));
users.forEach(user -> {
System.out.println(user.getFirstName() + " , " + user.getLastName() + " , " + user.getAge());
});
}
}
//here threshold,buyRange,targetPercentage are three keys on that i have sorted my arraylist
final Comparator<BasicDBObject>
sortOrder = new Comparator<BasicDBObject>() {
public int compare(BasicDBObject e1, BasicDBObject e2) {
int threshold = new Double(e1.getDouble("threshold"))
.compareTo(new Double(e2.getDouble("threshold")));
if (threshold != 0)
return threshold;
int buyRange = new Double(e1.getDouble("buyRange"))
.compareTo(new Double(e2.getDouble("buyRange")));
if (buyRange != 0)
return buyRange;
return (new Double(e1.getDouble("targetPercentage")) < new Double(
e2.getDouble("targetPercentage")) ? -1 : (new Double(
e1.getDouble("targetPercentage")) == new Double(
e2.getDouble("targetPercentage")) ? 0 : 1));
}
};
Collections.sort(objectList, sortOrder);
import com.google.common.collect.ComparisonChain;
/**
* @author radler
* Class Description ...
*/
public class Attribute implements Comparable<Attribute> {
private String type;
private String value;
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
@Override
public String toString() {
return "Attribute [type=" + type + ", value=" + value + "]";
}
@Override
public int compareTo(Attribute that) {
return ComparisonChain.start()
.compare(this.type, that.type)
.compare(this.value, that.value)
.result();
}
}