Сравнить несколькими методами в Java compareTo?

Я не думаю, что это лучший способ выразить это название, но я не могу придумать лучшего способа сформулировать это слово. Вот моя проблема: мне нужно написать метод, который сравнивает несколько разных способов. Если фамилии совпадают, мне нужно сравнить по имени. Если имена совпадают, то мне нужно отсортировать по разделам. Какой способ сортировки структуры данных в этой иерархии был бы наиболее эффективным? Вот что у меня сейчас есть, и я думаю, что понимаю, почему это не работает, но я не могу придумать другой способ написать эту функцию:

//Student class structure, each field has a public get/set method
public class Student implements Comparable<Student>
{
  private String fname;
  private String lname;
  private int section;
}

//My current compareTo method
@Override
public int compareTo(Student s)
{
  /*
    -compare by last name
    -if the same, compare by first name
    -if the same, compare by section
  */

  String slast = s.getLastName();

  if(lname.compareTo(slast) == 0)
  {
    String sfirst = s.getFirstName();

    if(fname.compareTo(sfirst) == 0)
    {
      int sclass = s.getSection();

      return Integer.compare(section, sclass);
    }

    else
    {
      return fname.compareTo(sfirst);
    }
  }

  else
  {
    return lname.compareTo(slast);
  }
}

person John Porterfield    schedule 21.01.2020    source источник
comment
Это работает, и вы ищете лучший способ сделать это, или у вас есть проблемы с этим кодом? Если это последнее, пожалуйста, добавьте более подробную информацию о том, какой ввод не дает ожидаемого результата.   -  person user7    schedule 21.01.2020
comment
Отвечает ли это на ваш вопрос? Как сравнивать объекты по нескольким полям   -  person Scratte    schedule 21.01.2020
comment
@ user7 моя ошибка, моя проблема с моим кодом заключалась в проблеме, которой я не ожидал, удаляю вопрос, моя проблема.   -  person John Porterfield    schedule 21.01.2020
comment
@Scratte, который действительно помогает, спасибо   -  person John Porterfield    schedule 21.01.2020


Ответы (3)


Вы можете создать компаратор для своего класса Student следующим образом:

Comparator<Student> comparator = Comparator
        .comparing(Student::getLastName)
        .thenComparing(Student::getFirstName)
        .thenComparing(Student::getSection);

А затем используйте этот компаратор (вместо реализации интерфейса Comparable) для сортировки списка с объектами Student или для создания TreeMap с этими объектами:

Collections.sort(listOfStudents, comparator);
TreeMap<Student> mapOfStudents = new TreeMap<>(comparator);
person ardenit    schedule 21.01.2020

Вам не нужно использовать геттеры или сеттеры, если вы переопределяете compareTo. Вы также можете отказаться от операторов else / return, поскольку они являются операторами терминального возврата, и просто используйте return.

@Override
public int compareTo(Student s) {
    if (lname.compareTo(s.lname) == 0) {
        if (fname.compareTo(s.fname) == 0) {
            return section.compareTo(s.section);
        }
        return fname.compareTo(s.fname);
    }
    return lname.compareTo(s.lname);
}
person Compass    schedule 21.01.2020

Ваш код мне кажется правильным.

Какой способ сортировки структуры данных в этой иерархии был бы наиболее эффективным?

Что ж, стоит упомянуть, что вы потенциально делаете первые два сравнения (имя и фамилия) несколько раз.

if(lname.compareTo(slast) == 0)
{
    //...
}
else
{
    return lname.compareTo(slast);
}

Должно быть совершенно очевидно, что вы делаете lname.compareTo(slast) дважды. Вместо этого вы можете сохранить результат в переменной.

int lastNameComparison = lname.compareTo(slast);
if(lastNameComparison == 0)
{
    //...
}
else
{
    return lastNameComparison;
}

Это вопрос стиля, но я бы не стал хранить результат геттеров в переменных. Просто позвоните им, когда они вам понадобятся.

Комбинируя оба вышеперечисленных пункта, вы получаете:

int lastNameComparison = lname.compareTo(s.getLastName();
if (lastNameComparison == 0)
{
    int firstNameComparison = fname.compareTo(s.getFirstName());
    if (firstNameComparison == 0)
    {
        return Integer.compare(section, s.getSection());
    }
    else
    {
        return firstNameComparison;
    }
}
else
{
    return lastNameComparison;
}

Вложенность довольно уродливая, и если нам нужно добавить еще один критерий, все станет еще хуже.

Мы можем решить эту проблему, инвертируя условия и используя несколько операторов возврата.

int lastNameComparison = lname.compareTo(s.getLastName());
if (lastNameComparison != 0) return lastNameComparison;

// Last names must be equal
int firstNameComparison = fname.compareTo(s.getFirstName());
if (firstNameComparison != 0) return firstNameComparison;

// First names must be equal
return Integer.compare(section, s.getSection());

Я бы лично использовал декларативный стиль написания этого, но если этот код предназначен для задания, это, вероятно, не то, что они ждем.

person Community    schedule 21.01.2020