为了账号安全,请及时绑定邮箱和手机立即绑定

具有多个字段的Collections.sort

/ 猿问

具有多个字段的Collections.sort

喵喵时光机 2019-11-11 16:06:44

我有一个包含三个字段(所有字符串类型)的“报表”对象列表-


ReportKey

StudentNumber

School

我有一个排序代码,就像-


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());

      }


});

由于某种原因,我没有排序顺序。一个建议在字段之间插入空格,但是为什么呢?


您认为代码有什么问题吗?


查看完整描述

3 回答

?
慕田峪4524236

您认为代码有什么问题吗?


是。为什么在比较它们之前将三个字段加在一起?


我可能会做这样的事情:(假设字段按您希望排序的顺序排列)


@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;

}


查看完整回答
反对 回复 2019-11-11
?
慕雪6442864

使用Java 8 lambda

Java 8通过lambda很好地解决了这个问题(尽管Guava和Apache Commons可能仍然提供更大的灵活性):


Collections.sort(reportList, Comparator.comparing(Report::getReportKey)

            .thenComparing(Report::getStudentNumber)

            .thenComparing(Report::getSchool));

感谢@gaoagong在下面的回答。


凌乱而令人费解:手工排序

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);  

    }  

});  

这需要大量的输入,维护并且容易出错。


反射方式:使用BeanComparator排序

ComparatorChain chain = new ComparatorChain(Arrays.asList(

   new BeanComparator("size"), 

   new BeanComparator("nrOfToppings"), 

   new BeanComparator("name")));


Collections.sort(pizzas, chain);  

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


到达那里:使用Google Guava的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(); 

        */  

    }  

});  

这要好得多,但是对于最常见的用例,需要一些样板代码:默认情况下,空值的值应较小。对于空字段,您必须为Guava提供一个额外的指令,在这种情况下该怎么做。如果您想做特定的事情,但是通常您想要默认的大小写(即1,a,b,z,null),这是一种灵活的机制。


使用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();  

    }  

});  

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


从而

最终归结为风味和灵活性的需求(Guava的CompareChain)与简洁的代码(Apache的CompareToBuilder)。


奖金法

我发现了一个不错的解决方案,它在CodeReview中的优先级顺序中结合了多个比较器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));

    }

}

当然,Apache Commons Collections已经有了一个实用程序:


ComparatorUtils.chainedComparator(comparatorCollection)


Collections.sort(list, ComparatorUtils.chainedComparator(comparators));


查看完整回答
反对 回复 2019-11-11
?
萧十郎

这是一个老问题,所以我看不到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 + '\'' +

                   '}';

        }

    }

}


查看完整回答
反对 回复 2019-11-11

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信