按属性对自定义对象的ArrayList进行排序

1282

我了解到可以使用Comparator对ArrayList进行排序,但在所有示例中,人们都使用了compareTo方法,这是一种针对字符串的方法。

我想要通过一个自定义对象属性(Date类型的getStartDay()方法)来对ArrayList进行排序。通常,我会使用item1.getStartDate().before(item2.getStartDate()) 进行比较,所以我在想,是否可以编写类似以下方式的代码:

public class CustomComparator {
    public boolean compare(Object object1, Object object2) {
        return object1.getStartDate().before(object2.getStartDate());
    }
}

public class RandomName {
    ...
    Collections.sort(Database.arrayList, new CustomComparator);
    ...
}

4
相关链接:https://dev59.com/NXI-5IYBdhLWcg3whYsr - BalusC
2
此帖中@Yishai的回答展示了枚举在自定义排序和分组排序(多个参数)中的优雅使用,利用比较器链接。 - gunalmel
29个回答

1718

由于 Date 实现了 Comparable 接口,因此它像 String 一样具有 compareTo 方法。

因此,您的自定义 Comparator 可以如下所示:

public class CustomComparator implements Comparator<MyObject> {
    @Override
    public int compare(MyObject o1, MyObject o2) {
        return o1.getStartDate().compareTo(o2.getStartDate());
    }
}

"compare()方法必须返回一个int,因此你无法像你计划的那样直接返回一个boolean

你的排序代码几乎就像你写的一样:

"
Collections.sort(Database.arrayList, new CustomComparator());

如果您不需要重用比较器,一个稍微更短的编写方式是将其编写为内联匿名类:

Collections.sort(Database.arrayList, new Comparator<MyObject>() {
    @Override
    public int compare(MyObject o1, MyObject o2) {
        return o1.getStartDate().compareTo(o2.getStartDate());
    }
});

以来

现在,您可以使用 lambda 表达式Comparator 缩短最后一个示例的代码:

Collections.sort(Database.arrayList, 
                        (o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));

List有一个sort(Comparator)方法,因此您可以进一步缩短代码:

Database.arrayList.sort((o1, o2) -> o1.getStartDate().compareTo(o2.getStartDate()));

这是一个非常常见的习语,有一个内置方法生成一个带有Comparable键的类的Comparator
Database.arrayList.sort(Comparator.comparing(MyObject::getStartDate));

所有这些都是等效形式。


41
+1 是因为提到应该返回 int 类型并且最好使用 Date#compareTo() 方法,我不明白为什么这个回答没有得到更多的赞。这个链接也可能会有用:Sun.com 的对象排序教程 - BalusC
7
我认为最好的答案也应该包括在Java 8中正确的做法。 Collections.sort(list, Comparator.comparing(MyObject::getStartDate)); 这种写法更易读,而且出错的可能性更小。很容易写成 return o1.getStartDate().compareTo(o1.getStartDate()); - Kuba
2
比较器类应该是静态的 :) - Jarmez De La Rocha
4
最好使用 List.sort() - shmosel
3
这个解决方案不适用于 Android API <24。你们知道这个问题的解决方案吗? - Jim Clermonts
显示剩余10条评论

201

具有自然排序的类(例如 Number 类)应该实现 Comparable 接口,而没有自然排序的类(例如 Chair 类)应该提供 Comparator(或匿名 Comparator 类)。

以下是两个例子:

public class Number implements Comparable<Number> {
    private int value;

    public Number(int value) { this.value = value; }
    public int compareTo(Number anotherInstance) {
        return this.value - anotherInstance.value;
    }
}

public class Chair {
    private int weight;
    private int height;

    public Chair(int weight, int height) {
        this.weight = weight;
        this.height = height;
    }
    /* Omitting getters and setters */
}
class ChairWeightComparator implements Comparator<Chair> {
    public int compare(Chair chair1, Chair chair2) {
        return chair1.getWeight() - chair2.getWeight();
    }
}
class ChairHeightComparator implements Comparator<Chair> {
    public int compare(Chair chair1, Chair chair2) {
        return chair1.getHeight() - chair2.getHeight();
    }
}

使用方法:

List<Number> numbers = new ArrayList<Number>();
...
Collections.sort(numbers);

List<Chair> chairs = new ArrayList<Chair>();
// Sort by weight:
Collections.sort(chairs, new ChairWeightComparator());
// Sort by height:
Collections.sort(chairs, new ChairHeightComparator());

// You can also create anonymous comparators;
// Sort by color:
Collections.sort(chairs, new Comparator<Chair>() {
    public int compare(Chair chair1, Chair chair2) {
        ...
    }
});

我试过了 - 但当我想在任何其他类中访问比较器类ChairWeightComparator时,我无法访问该类(当然不行,因为它不是public)。我需要在单独的文件中创建一个新的public ChairWeightComparator类吗?- 我是第一个在3年后尝试这样做的人,还是我错过了什么? - user387184
@user387184 - 只需将其公开并放入自己的文件中(最好是自己的包),您就可以在项目的任何地方使用它。无需创建额外的类! - Björn
你的意思是创建一个新文件 - 不是一个类,并放入以下代码:"class ChairWeightComparator implements Comparator<Chair> {...." ? - user387184
@user387184,没错 - 但是在class前面加上关键字public - Björn
这在我的笔记本电脑上无法运行。它显示int不能被取消引用的错误。如何纠正? - Alex

164

如果要对 ArrayList 进行排序,您可以使用以下代码片段:

Collections.sort(studList, new Comparator<Student>(){
    public int compare(Student s1, Student s2) {
        return s1.getFirstName().compareToIgnoreCase(s2.getFirstName());
    }
});

1
有人在使用lambda吗?https://dev59.com/xnE85IYBdhLWcg3wbS1h#25686715 - Sorter
那个排序了,但是每个元素的值都变成了两倍。 - Samir
这在我的笔记本电脑上无法运行。它显示int不能被取消引用的错误。如何纠正? - Alex

47

JAVA 8 Lambda表达式

Collections.sort(studList, (Student s1, Student s2) ->{
        return s1.getFirstName().compareToIgnoreCase(s2.getFirstName());
});

OR

Comparator<Student> c = (s1, s2) -> s1.firstName.compareTo(s2.firstName);
studList.sort(c)

3
studList 做按照 Student 对象的 firstName 属性进行排序的操作,可以使用以下代码:Collections.sort(studList, Comparator.comparing(Student::getFirstName)); - Holger
6
或者 studList.sort(Comparator.comparing(Student::getFirstName)); 可以将一个包含学生对象的列表按照学生的名字属性进行升序排序。 - Alexis C.
在您的情况下,排序顺序将始终是升序。在我的示例中,我也已经处理了排序顺序。谢谢各位先生。 - Sorter

45

是的,你可以这样做。比较两个对象有两种选择:使用Comparable接口或Comparator接口。

这两个接口允许不同的行为。Comparable允许你使对象表现得像你刚才描述的字符串(实际上,String实现了Comparable)。另一个接口——Comparator允许你做你想做的事情。你可以这样写:

Collections.sort(myArrayList, new MyComparator());

这将导致Collections.sort方法使用您的比较器进行排序。如果ArrayList中的对象实现了comparable接口,您可以采用以下方式:

Collections.sort(myArrayList);

Collections类中包含许多有用且常见的工具。


这在我的笔记本电脑上无法运行。它显示int不能被取消引用的错误。如何纠正? - Alex

39

使用Java 8,您可以为比较器使用方法引用:

import static java.util.Comparator.comparing;

Collections.sort(list, comparing(MyObject::getStartDate));

@user387184 很遗憾,安卓不支持Java 8,尽管可能有一些解决方法(我没有测试过)。 - assylias

15

随着技术的不断出现,答案将会随时间而变化。我查看了LambdaJ,它似乎非常有趣。

你可以尝试使用LambdaJ解决这些任务。你可以在这里找到它:http://code.google.com/p/lambdaj/

这里有一个例子:

迭代排序

List<Person> sortedByAgePersons = new ArrayList<Person>(persons);
Collections.sort(sortedByAgePersons, new Comparator<Person>() {
        public int compare(Person p1, Person p2) {
           return Integer.valueOf(p1.getAge()).compareTo(p2.getAge());
        }
});

使用lambda进行排序

List<Person> sortedByAgePersons = sort(persons, on(Person.class).getAge()); 

当然,这种美丽会影响性能(平均2倍),但你能找到更易读的代码吗?


那个排序但是每个元素都给了双倍的值,如何避免它? - Samir
@Sam,不应该出现这种情况...它的运行结果是符合预期的。除非你使用了一个有缺陷的新版本,我建议你在论坛中发布它。无论如何,这个答案是在Java 8之前发布的,如果你使用它,那么它比使用lambdaj要好得多。 - Federico Piazza
我必须在foreach循环中删除偶数项,否则它会给我每个内容的两倍。 - Samir

13

函数和方法参考

Collections.sort 方法可以使用你传递的 ComparatorList 进行排序。该 Comparator 可以使用 Comparator.comparing 方法实现,其中你可以将一个 方法引用 作为必要的 Function 传递。幸运的是,实际代码比这个说明更简单、更短。

对于Java 8:

Collections.sort(list, comparing(ClassName::getName));

或者

Collections.sort(list, comparing(ClassName::getName).reversed());

另一种方法是

Collections.sort(list, comparing(ClassName::getName, Comparator.nullsLast(Comparator.naturalOrder())));

1
调用需要API级别24。 - akshay bhange

13
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;

public class test {

public static class Person {
    public String name;
    public int id;
    public Date hireDate;

    public Person(String iname, int iid, Date ihireDate) {
        name = iname;
        id = iid;
        hireDate = ihireDate;
    }

    public String toString() {
        return name + " " + id + " " + hireDate.toString();
    }

    // Comparator
    public static class CompId implements Comparator<Person> {
        @Override
        public int compare(Person arg0, Person arg1) {
            return arg0.id - arg1.id;
        }
    }

    public static class CompDate implements Comparator<Person> {
        private int mod = 1;
        public CompDate(boolean desc) {
            if (desc) mod =-1;
        }
        @Override
        public int compare(Person arg0, Person arg1) {
            return mod*arg0.hireDate.compareTo(arg1.hireDate);
        }
    }
}

public static void main(String[] args) {
    // TODO Auto-generated method stub
    SimpleDateFormat df = new SimpleDateFormat("mm-dd-yyyy");
    ArrayList<Person> people;
    people = new ArrayList<Person>();
    try {
        people.add(new Person("Joe", 92422, df.parse("12-12-2010")));
        people.add(new Person("Joef", 24122, df.parse("1-12-2010")));
        people.add(new Person("Joee", 24922, df.parse("12-2-2010")));
    } catch (ParseException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    Collections.sort(people, new Person.CompId());
    System.out.println("BY ID");
    for (Person p : people) {
        System.out.println(p.toString());
    }

    Collections.sort(people, new Person.CompDate(false));
    System.out.println("BY Date asc");
    for (Person p : people) {
        System.out.println(p.toString());
    }
    Collections.sort(people, new Person.CompDate(true));
    System.out.println("BY Date desc");
    for (Person p : people) {
        System.out.println(p.toString());
    }

}

}

9
欢迎来到Stack Overflow。这个问题在一段时间前已经得到了回答。在重新激活旧帖子之前,请确保您的回复对该帖子有重要的补充。 - Leigh
2
请在您的答案中添加解释。 - Arashsoft
1
只需编译并运行即可。代码本身就是注释和解释。 - CharlesW

10

使用JAVA 8进行英文字母排序的最佳易用方法

类实现

public class NewspaperClass implements Comparable<NewspaperClass>{
   public String name;

   @Override
   public int compareTo(NewspaperClass another) {
      return name.compareTo(another.name);
   }
}

排序

  Collections.sort(Your List);
如果您想按包含非英语字符的字母表进行排序,可以使用Locale...以下代码使用土耳其字符排序... 类实现
public class NewspaperClass implements Comparator<NewspaperClass> {
   public String name;
   public Boolean isUserNewspaper=false;
   private Collator trCollator = Collator.getInstance(new Locale("tr_TR"));



   @Override
   public int compare(NewspaperClass lhs, NewspaperClass rhs) {
      trCollator.setStrength(Collator.PRIMARY);
      return trCollator.compare(lhs.name,rhs.name);
   }
}

排序

Collections.sort(your array list,new NewspaperClass());

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接