Collections.sort包含多个字段

Mil*_*abo 71 java sorting collections

我有一个包含三个字段的"Report"对象列表(All String type) -

ReportKey
StudentNumber
School
Run Code Online (Sandbox Code Playgroud)

我有一个排序代码像 -

Collections.sort(reportList, new Comparator<Report>() {

@Override
public int compare(final Report record1, final Report record2) {
      return (record1.getReportKey() + record1.getStudentNumber() + record1.getSchool())                      
        .compareTo(record2.getReportKey() + record2.getStudentNumber() + record2.getSchool());
      }

});
Run Code Online (Sandbox Code Playgroud)

出于某种原因,我没有排序顺序.建议在场之间放置空格,但为什么呢?

你看到代码有什么问题吗?

Jas*_*n S 120

你看到代码有什么问题吗?

是.在比较它们之前,为什么要将这三个字段一起添加?

我可能会这样做:(假设字段按照您希望对它们进行排序的顺序)

@Override public int compare(final Report record1, final Report record2) {
    int c;
    c = record1.getReportKey().compareTo(record2.getReportKey());
    if (c == 0)
       c = record1.getStudentNumber().compareTo(record2.getStudentNumber());
    if (c == 0)
       c = record1.getSchool().compareTo(record2.getSchool());
    return c;
}
Run Code Online (Sandbox Code Playgroud)

  • 我不认为你理解[`a.compareTo(b)`](https://docs.oracle.com/javase/7/docs/api/java/lang/Comparable.html#compareTo(T)); 惯例是值0表示相等,负整数表示`a <b`,正整数表示`a> b`表示`Comparable``a`和`b`. (9认同)

Ben*_*ema 66

(来自基于多个字段对Java中的对象列表进行排序的方法)

这个要点中的工作代码

凌乱而错综复杂:手工排序

Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
            .thenComparing(Report::getStudentNumber)
            .thenComparing(Report::getSchool));
Run Code Online (Sandbox Code Playgroud)

这需要大量的打字,维护并且容易出错.

反思方式:使用BeanComparator进行排序

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);  
    }  
});  
Run Code Online (Sandbox Code Playgroud)

显然,这更简洁,但更容易出错,因为您通过使用字符串而无法直接引用字段(没有类型安全,自动重构).现在,如果重命名一个字段,编译器甚至不会报告问题.此外,由于此解决方案使用反射,因此排序速度要慢得多.

到达目的地:使用Google Guava的ComparisonChain进行排序

ComparatorChain chain = new ComparatorChain(Arrays.asList(
   new BeanComparator("size"), 
   new BeanComparator("nrOfToppings"), 
   new BeanComparator("name")));

Collections.sort(pizzas, chain);  
Run Code Online (Sandbox Code Playgroud)

这样做要好得多,但是对于最常见的用例需要一些样板代码:默认情况下,null值的值应该更少.对于null-fields,你必须向Guava提供一个额外的指令,在这种情况下该做什么.如果您想要执行特定的操作,这是一种灵活的机制,但通常您需要默认情况(即1,a,b,z,null).

使用Apache Commons CompareToBuilder进行排序

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(); 
        */  
    }  
});  
Run Code Online (Sandbox Code Playgroud)

与Guava的ComparisonChain一样,此库类可以在多个字段上轻松排序,但也定义了空值的默认行为(即.1,a,b,z,null).但是,除非您提供自己的比较器,否则您也无法指定其他任何内容.

从而

最终,它归结为味道和灵活性的需求(Guava的ComparisonChain)与简洁的代码(Apache的CompareToBuilder).

奖金方法

我发现了一个很好的解决方案,结合了多个比较优先的顺序对代码审查MultiComparator:

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();  
    }  
});  
Run Code Online (Sandbox Code Playgroud)

Ofcourse Apache Commons Collections已经有了这个功能:

ComparatorUtils.chainedComparator(comparatorCollection)

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));
    }
}
Run Code Online (Sandbox Code Playgroud)


Col*_*inD 44

我会想办法让使用比较番石榴ComparisonChain:

public class ReportComparator implements Comparator<Report> {
  public int compare(Report r1, Report r2) {
    return ComparisonChain.start()
        .compare(r1.getReportKey(), r2.getReportKey())
        .compare(r1.getStudentNumber(), r2.getStudentNumber())
        .compare(r1.getSchool(), r2.getSchool())
        .result();
  }
}
Run Code Online (Sandbox Code Playgroud)


gao*_*ong 17

这是一个老问题所以我没有看到Java 8的等价物.以下是此特定案例的示例.

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Compares multiple parts of the Report object.
 */
public class SimpleJava8ComparatorClass {

    public static void main(String[] args) {
        List<Report> reportList = new ArrayList<>();
        reportList.add(new Report("reportKey2", "studentNumber2", "school1"));
        reportList.add(new Report("reportKey4", "studentNumber4", "school6"));
        reportList.add(new Report("reportKey1", "studentNumber1", "school1"));
        reportList.add(new Report("reportKey3", "studentNumber2", "school4"));
        reportList.add(new Report("reportKey2", "studentNumber2", "school3"));

        System.out.println("pre-sorting");
        System.out.println(reportList);
        System.out.println();

        Collections.sort(reportList, Comparator.comparing(Report::getReportKey)
            .thenComparing(Report::getStudentNumber)
            .thenComparing(Report::getSchool));

        System.out.println("post-sorting");
        System.out.println(reportList);
    }

    private static class Report {

        private String reportKey;
        private String studentNumber;
        private String school;

        public Report(String reportKey, String studentNumber, String school) {
            this.reportKey = reportKey;
            this.studentNumber = studentNumber;
            this.school = school;
        }

        public String getReportKey() {
            return reportKey;
        }

        public void setReportKey(String reportKey) {
            this.reportKey = reportKey;
        }

        public String getStudentNumber() {
            return studentNumber;
        }

        public void setStudentNumber(String studentNumber) {
            this.studentNumber = studentNumber;
        }

        public String getSchool() {
            return school;
        }

        public void setSchool(String school) {
            this.school = school;
        }

        @Override
        public String toString() {
            return "Report{" +
                   "reportKey='" + reportKey + '\'' +
                   ", studentNumber='" + studentNumber + '\'' +
                   ", school='" + school + '\'' +
                   '}';
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Jon*_*eet 13

如果你想按报告密钥,学生编号,然后学校排序,你应该做这样的事情:

public class ReportComparator implements Comparator<Report>
{
    public int compare(Report r1, Report r2)
    {
        int result = r1.getReportKey().compareTo(r2.getReportKey());
        if (result != 0)
        {
            return result;
        }
        result = r1.getStudentNumber().compareTo(r2.getStudentNumber());
        if (result != 0)
        {
            return result;
        }
        return r1.getSchool().compareTo(r2.getSchool());
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,假设这些值都不能为空 - 如果您需要为报告,报告密钥,学号或学校允许空值,则会变得更复杂.

虽然您可以使用空格来使字符串连接版本工作,但如果您有奇数据本身包含空格等,它仍会在奇怪的情况下失败.上面的代码是您想要的逻辑代码...首先按报告键进行比较,然后如果报告密钥相同,只会打扰学生编号等.

  • 虽然这段代码没有"错误",但我理解它.我更喜欢Jason的实现,因为它似乎更容易理解,因为他只有一个return语句. (6认同)

小智 7

我建议使用Java 8 Lambda方法:

List<Report> reportList = new ArrayList<Report>();
reportList.sort(Comparator.comparing(Report::getRecord1).thenComparing(Report::getRecord2));
Run Code Online (Sandbox Code Playgroud)


FrV*_*aBe 5

如果 StudentNumber 是数字,则不会按数字排序,而是按字母数字排序。不要指望

"2" < "11"
Run Code Online (Sandbox Code Playgroud)

这将是:

"11" < "2"
Run Code Online (Sandbox Code Playgroud)


Lak*_*ani 5

在Java8中使用多个字段进行排序

package com.java8.chapter1;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import static java.util.Comparator.*;



 public class Example1 {

    public static void main(String[] args) {
        List<Employee> empList = getEmpList();


        // Before Java 8 
        empList.sort(new Comparator<Employee>() {

            @Override
            public int compare(Employee o1, Employee o2) {
                int res = o1.getDesignation().compareTo(o2.getDesignation());
                if (res == 0) {
                    return o1.getSalary() > o2.getSalary() ? 1 : o1.getSalary() < o2.getSalary() ? -1 : 0;
                } else {
                    return res;
                }

            }
        });
        for (Employee emp : empList) {
            System.out.println(emp);
        }
        System.out.println("---------------------------------------------------------------------------");

        // In Java 8

        empList.sort(comparing(Employee::getDesignation).thenComparing(Employee::getSalary));
        empList.stream().forEach(System.out::println);

    }
    private static List<Employee> getEmpList() {
        return Arrays.asList(new Employee("Lakshman A", "Consultent", 450000),
                new Employee("Chaitra S", "Developer", 250000), new Employee("Manoj PVN", "Developer", 250000),
                new Employee("Ramesh R", "Developer", 280000), new Employee("Suresh S", "Developer", 270000),
                new Employee("Jaishree", "Opearations HR", 350000));
    }
}

class Employee {
    private String fullName;
    private String designation;
    private double salary;

    public Employee(String fullName, String designation, double salary) {
        super();
        this.fullName = fullName;
        this.designation = designation;
        this.salary = salary;
    }

    public String getFullName() {
        return fullName;
    }

    public String getDesignation() {
        return designation;
    }

    public double getSalary() {
        return salary;
    }

    @Override
    public String toString() {
        return "Employee [fullName=" + fullName + ", designation=" + designation + ", salary=" + salary + "]";
    }

}
Run Code Online (Sandbox Code Playgroud)