sort object list java
Java 8 comparator nullFirst naturalOrder confuso (3)
El comparador de "orden natural", que es lo que obtiene cuando utiliza la comparing
con un solo parámetro, no maneja nulos. (No estoy seguro de dónde surgió la idea de que sí.) El "orden natural" de una clase Comparable
se define mediante el compareTo()
, que se utiliza así:
obj1.compareTo(obj2)
Obviamente, esto no funcionará si obj1
es nulo; para String
, también lanzará una excepción de que obj2
es nulo.
El método naturalOrder()
devuelve un Comparator
que compara dos objetos. El javadoc dice explícitamente que este comparador arroja NullPointerException
al comparar null.
El método nullsFirst()
(y nullsLast()
manera similar) básicamente transforma un Comparator
en un nuevo Comparator
. Pones un comparador que puede arrojar una excepción si intenta comparar nulo, y arroja un nuevo comparador que funciona de la misma manera, excepto que permite argumentos nulos. Es por eso que necesita un parámetro para nullsFirst
ya que crea un nuevo comparador sobre un comparador existente y le dice cuál es el comparador existente.
Entonces, ¿por qué no te da el orden natural si dejas fuera el parámetro? Porque ellos no lo definieron de esa manera. nullsFirst
se define en javadoc para tomar un parámetro:
static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator)
Creo que si los diseñadores quisieran, podrían haber agregado una sobrecarga que no requiere parámetros:
static <T> Comparator<T> nullsFirst() // note: not legal
eso sería lo mismo que usar nullsFirst(naturalOrder())
. Pero no lo hicieron, así que no puedes usarlo así.
esto puede ser una pregunta simple, pero me gustaría entenderlo claramente ...
Tengo un código como este:
public final class Persona
{
private final int id;
private final String name
public Persona(final int id,final String name)
{
this.id = id;
this.name = name;
}
public int getId(){return id;}
public String getName(){return name;}
@Override
public String toString(){return "Persona{" + "id=" + id + ", name=" + name+''}'';}
}
Y estoy probando este código:
import static java.util.Comparator.*;
private void nullsFirstTesting()
{
final Comparator<Persona>comparator = comparing(Persona::getName,nullsFirst(naturalOrder()));
final List<Persona>persons = Arrays.asList(new Persona(1,"Cristian"),new Persona(2,"Guadalupe"),new Persona(3,"Cristina"),new Persona(4,"Chinga"),new Persona(5,null));
persons
.stream()
.sorted(comparator)
.forEach(System.out::println);
}
Esto muestra los siguientes resultados:
Persona{id=5, name=null}
Persona{id=4, name=Chinga}
Persona{id=1, name=Cristian}
Persona{id=3, name=Cristina}
Persona{id=2, name=Guadalupe}
Estos resultados están bien para mí, pero tengo un problema para entenderlo.
Cuando ignoro el new Persona(5,null)
y paso el comparador:
final Comparator<Persona>comparator = comparing(Persona::getName);
Funciona a las mil maravillas. Mi clasificación es por natural order of name property
. El problema surge cuando agrego el objeto con name=null
, solo pensé que necesitaría mi comparador de esta manera.
final Comparator<Persona>comparator = comparing(Persona::getName,nullsFirst());
Mi pensamiento era erróneo : "OK, cuando el nombre no es nulo, están ordenados en natural order of name
, al igual que el comparador anterior, y si son null
serán los primeros, pero mis nombres no nulos aún se ordenarán en orden natural ".
Pero el código correcto es este:
final Comparator<Persona>comparator = comparing(Persona::getName,nullsFirst(naturalOrder()));
No entiendo el parámetro nullsFirst
. Solo pensé que el natural order of name
explícitamente [predeterminado] incluso manejaría valores null
.
Pero los documentos dicen:
Devuelve un comparador null-friendly que considera que
null
es menor que non-null. Cuando ambos sonnull
, se consideran iguales. Si ambos son no nulos, elComparator
especificado se usa para determinar el orden. Si el comparador especificado esnull
, el comparador devuelto considera que todos los valores no nulos son iguales.
Esta línea: "Si ambos no son nulos, el Comparator
especificado se usa para determinar el orden".
Estoy confundido sobre cuándo y cómo se debe establecer explícitamente el orden natural o cuándo se deducen.
Tengo una lista de Empleado con Estudiante con nombre e id.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Comparator;
public class TestClass {
public static void main(String[] args) {
Student s1 = new Student("1","Nikhil");
Student s2 = new Student("1","*");
Student s3 = new Student("1",null);
Student s11 = new Student("2","Nikhil");
Student s12 = new Student("2","*");
Student s13 = new Student("2",null);
List<Student> list = new ArrayList<Student>();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s11);
list.add(s12);
list.add(s13);
list.sort(Comparator.comparing(Student::getName,Comparator.nullsLast(Comparator.naturalOrder())));
for (Iterator iterator = list.iterator(); iterator.hasNext();) {
Student student = (Student) iterator.next();
System.out.println(student);
}
}
}
Produce salida como
Student [name=*, id=1]
Student [name=*, id=2]
Student [name=Nikhil, id=1]
Student [name=Nikhil, id=2]
Student [name=null, id=1]
Student [name=null, id=2]
Tratar:
final Comparator<Persona> comparator =
comparing(Persona::getName, nullsFirst(naturalOrder()));