0

Hace poco leí una pregunta en Stack Overflow en español sobre cómo ordenar una serie de Monstruos que poseen distintas variables, además de una que coincide en todos, para un juego de rol.

El ordenamiento se me hizo bastante interesante además de un poco complejo en cuanto a conocimientos de programación y del lenguaje y me quedó la duda sobre cómo se puede hacer el ordenamiento consecutivo de un arreglo o vector de clases hijas que heredan de un mismo padre, con base en atributos propios de las clases hijas como de las de la clase padre.

Para empezar la investigación propuse el algoritmo más general posible:

  1. Se ordena la lista conforme a la primera prioridad
  2. Aquellas que coincidan (cuyo primer orden de prioridad sea el mismo) se ordenarán de acuerdo al orden i-ésimo de las siguientes prioridades.

El caso propuesto es ordenar un vector de personas (maestros y estudiantes) de acuerdo a características compartidas y específicas de cada una.

Estructura de la herencia

Se tiene una clase Person de la cual heredan las clases Student y Teacher

Clase padre

protected class Person {
    private final String name;
    private final String lastname;
    private final int age;

    public Person(String name, String lastname, int age) {
        this.name = name;
        this.lastname = lastname;
        this.age = age;
    }

    public String getLastname() {
        return lastname;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

Clases hijas

private static class Teacher extends Person {
    private int id;
    private final String specialization;

    public Teacher(String name, String lastname, int age, int id, String specialization) {
        super(name, lastname, age);
        this.id = id;
        this.specialization = specialization;
    }

    public int getId() {
        return id;
    }

    @Override
    public String toString(){
        return String.format(
                "%s %s %s\n%s %d\n%s %s\n%s %d",
                "Teacher:",
                getName(),
                getLastname(),
                "Age:",
                getAge(),
                "Specialization:",
                specialization,
                "id:",
                id
        );
    }
}
private static class Student extends Person {
    private double academic_average;
    private String group;

    public Student(String name, String lastname, int age, double academic_average, String group) {
        super(name, lastname, age);
        this.academic_average = academic_average;
        this.group = group;
    }

    public String getGroup() {
        return group;
    }

    @Override
    public String toString(){
        return String.format(
                "%s %s %s\n%s %d\n%s %s",
                "Student:",
                getName(),
                getLastname(),
                "Age:",
                getAge(),
                "Group:",
                group
        );
    }
}

Ordenamiento

Se establece el orden de prioridad que tendrá nuestro ordenamiento el cual es:

  1. Apellido (lastname)
  2. Nombre (name)
  3. Personalizado de acuerdo a:
    • Student: Grupo (group)
    • Teacher: ID (id)

Es decir, tenemos un vector de Personas que serán ordenadas por sus apellidos, luego por su nombre y luego por el ID

Es decir, si existen dos personas con los mismos apellidos se ordenarán esas dos por nombre y si fuera el caso de que siguieran repitiéndose, se ordenarían: si son estudiantes por grupo, si son maestros por ID.

Eduardo Jiménez
  • 2,093
  • 2
  • 6
  • 26

1 Answers1

0

Para empezar, la solución propuesta se logra mediante la concatenación de Comparator e implementados en ArrayList.sort (Se usará un ArrayList en esta respuesta)

A comparison function, which imposes a total ordering on some collection of objects. Comparators can be passed to a sort method (such as Collections.sort or Arrays.sort) to allow precise control over the sort order.


Una función de comparación, que impone un ordenamiento total en alguna colección de objetos. Los comparadores se pueden pasar a un método de clasificación (como Collections.Sort o Arrays.Sort) para permitir un control preciso sobre el orden de clasificación.

Definiendo el ordenamiento

Primero hay que recordar el orden de prioridad que tendrá nuestro ordenamiento el cual es:

  1. Apellido (lastname)
  2. Nombre (name)
  3. Personalizado de acuerdo a:
    • Student: Grupo (group)
    • Teacher: ID (id)

Para lograr el ordenamiento usaremos los métodos String.compareToIgnoreCase e Integer.compare, los cuales retornan:

  • Número negativo si el primer argumento es menor al segundo
  • Cero si los argumentos son iguales
  • Número positivo si el primer argumento es mayor al segundo

En código

Para los atributos que son heredados de la clase padre no hay nada demasiado complejo:

protected static class LastNameCompare implements Comparator<Person> {
    @Override
    public int compare(Person o1, Person o2) {
        return o1.getLastname().compareToIgnoreCase(o2.getLastname());
    }
}

protected static class NameCompare implements Comparator<Person> {
    @Override
    public int compare(Person o1, Person o2) {
        return o1.getName().compareToIgnoreCase(o2.getName());
    }
}

Para los atributos propios de cada clase tenemos que tener cuidado para seleccionar y castear el objeto correctamente con ayuda de instanceof

protected static class SelectiveComparator implements Comparator<Person> {
    @Override
    public int compare(Person o1, Person o2) {
        if (o1 instanceof Student && o2 instanceof Student) {
            return ((Student) o1).getGroup().compareToIgnoreCase(((Student) o2).getGroup());
        } else if (o1 instanceof Teacher && o2 instanceof Teacher) {
            return Integer.compare(((Teacher) o1).getId(), ((Teacher) o2).getId());
        }
        return 0;
    }
}

Como vemos, en el if se compara si los objetos a comparar son la misma clase (sea Student o Teacher) o1 instanceof Student && o2 instanceof Student

Finalmente se hace una comparación concatenada.

public static class ChainedComparator implements Comparator<Person> {

    private final List<Comparator<Person>> comparators;

    @SafeVarargs
    public ChainedComparator(Comparator<Person>... comparators) {
        this.comparators = Arrays.asList(comparators);
    }

    @Override
    public int compare(Person object1, Person object2) {
        int result;
        for (Comparator<Person> comparator : comparators) {
            result = comparator.compare(object1, object2);
            if (result != 0) {
                return result;
            }
        }
        return 0;
    }
}

En esta clase se reciben varargs como constructor, esto para mejorar la mantenibilidad del código.

Y como vemos, dentro del for, se están haciendo las comparaciones, donde en el caso en que los valores que se comparen sean iguales (result = 0) se le aplica el siguiente comparador para ordenar, de lo contrario ya ni los aplica.

Cabe recalcar que el orden de los varargs van a determinar el orden deseado de ordenamiento.

Ejemplo completo

import java.util.*;

public class Main {

    public static void main(String[] args) {

        Teacher some = new Teacher("Juana", "De Arco", 16, 501212, "Robótica");
        Student some0 = new Student("Michel", "Díaz", 16, 8.1, "6C01");
        Teacher some1 = new Teacher("Ricardo", "Zapata", 16, 503278, "Química");
        Student some2 = new Student("Pedro", "Alvarado", 16, 8.9, "4A01");
        Student some3 = new Student("Marcelo", "Alvarado", 17, 9.3, "2A01");
        Teacher some4 = new Teacher("Juana", "De Arco", 16, 501211, "Robótica");


        ArrayList<Person> people = new ArrayList<>(Arrays.asList(some, some0, some1, some2, some3, some4));

        people.sort(
            new ChainedComparator(
                new LastNameCompare(),
                new NameCompare(),
                new SelectiveComparator()
            )
        );

        for (Person p : people) {
            System.out.println(p + "\n\n");
        }

    }

    protected static class SelectiveComparator implements Comparator<Person> {
        @Override
        public int compare(Person o1, Person o2) {
            if (o1 instanceof Student && o2 instanceof Student) {
                return ((Student) o1).getGroup().compareToIgnoreCase(((Student) o2).getGroup());
            } else if (o1 instanceof Teacher && o2 instanceof Teacher) {
                return Integer.compare(((Teacher) o1).getId(), ((Teacher) o2).getId());
            }
            return 0;
        }
    }

    protected static class LastNameCompare implements Comparator<Person> {
        @Override
        public int compare(Person o1, Person o2) {
            return o1.getLastname().compareToIgnoreCase(o2.getLastname());
        }
    }

    protected static class NameCompare implements Comparator<Person> {
        @Override
        public int compare(Person o1, Person o2) {
            return o1.getName().compareToIgnoreCase(o2.getName());
        }
    }

    public static class ChainedComparator implements Comparator<Person> {

        private final List<Comparator<Person>> comparators;

        @SafeVarargs
        public ChainedComparator(Comparator<Person>... comparators) {
            this.comparators = Arrays.asList(comparators);
        }

        @Override
        public int compare(Person object1, Person object2) {
            int result;
            for (Comparator<Person> comparator : comparators) {
                result = comparator.compare(object1, object2);
                if (result != 0) {
                    return result;
                }
            }
            return 0;
        }
    }

    protected static class Person {
        private final String name;
        private final String lastname;
        private final int age;

        public Person(String name, String lastname, int age) {
            this.name = name;
            this.lastname = lastname;
            this.age = age;
        }

        public String getLastname() {
            return lastname;
        }

        public String getName() {
            return name;
        }

        public int getAge() {
            return age;
        }
    }

    private static class Teacher extends Person {
        private int id;
        private final String specialization;

        public Teacher(String name, String lastname, int age, int id, String specialization) {
            super(name, lastname, age);
            this.id = id;
            this.specialization = specialization;
        }

        public int getId() {
            return id;
        }

        @Override
        public String toString(){
            return String.format(
                    "%s %s %s\n%s %d\n%s %s\n%s %d",
                    "Teacher:",
                    getName(),
                    getLastname(),
                    "Age:",
                    getAge(),
                    "Specialization:",
                    specialization,
                    "id:",
                    id
            );
        }
    }

    private static class Student extends Person {
        private double academic_average;
        private String group;

        public Student(String name, String lastname, int age, double academic_average, String group) {
            super(name, lastname, age);
            this.academic_average = academic_average;
            this.group = group;
        }

        public String getGroup() {
            return group;
        }

        @Override
        public String toString(){
            return String.format(
                    "%s %s %s\n%s %d\n%s %s",
                    "Student:",
                    getName(),
                    getLastname(),
                    "Age:",
                    getAge(),
                    "Group:",
                    group
            );
        }
    }
}

Output

Student: Marcelo Alvarado
Age: 17
Group: 2A01


Student: Pedro Alvarado
Age: 16
Group: 4A01


Teacher: Juana De Arco
Age: 16
Specialization: Robótica
id: 501211


Teacher: Juana De Arco
Age: 16
Specialization: Robótica
id: 501212


Student: Michel Díaz
Age: 16
Group: 6C01


Teacher: Ricardo Zapata
Age: 16
Specialization: Química
id: 503278
Eduardo Jiménez
  • 2,093
  • 2
  • 6
  • 26