关于Java:按属性排序自定义对象的ArrayList

Sort ArrayList of custom Objects by property

我读过关于使用比较器对数组列表进行排序的文章,但是在所有的例子中,人们都使用了compareTo,根据一些研究,这是一种字符串的方法。

我想按自定义对象的属性之一对其数组列表进行排序:日期对象(getStartDay()号)。通常我用item1.getStartDate().before(item2.getStartDate())比较它们,所以我想知道我是否可以写如下的东西:

1
2
3
4
5
6
7
8
9
10
11
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);
    ...
}


由于Date实现了Comparable,它有一个compareTo方法,就像String一样。

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

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

compare()方法必须返回int,因此不能像计划的那样直接返回boolean

你的排序代码应该和你写的差不多:

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

如果不需要重用比较器,那么编写所有这些内容的一个稍短的方法是将其编写为一个内联匿名类:

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

自从Java8以来

现在您可以用一个lambda表达式为Comparator编写一个较短的示例:

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

List有一个sort(Comparator)方法,所以你可以进一步缩短:

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

这是一个很常见的习惯用法,有一个内置的方法可以为具有Comparable键的类生成Comparator

1
Database.arrayList.sort(Comparator.comparing(MyObject::getStartDate));

所有这些都是等价形式。


具有自然排序顺序的类(例如,类号)应实现可比较的接口,而没有自然排序顺序的类(例如,类椅子)应提供比较器(或匿名比较器类)。

两个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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();
    }
}

用途:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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) {
        ...
    }
});


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

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


是的,你可以。比较项有两个选项:可比较接口和比较器接口。

这两个接口都允许不同的行为。Comparable允许您使对象的行为类似于刚刚描述的字符串(实际上,字符串实现是可比较的)。第二个,比较器,允许你做你要求做的事情。您可以这样做:

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

这将导致collections.sort方法为其排序机制使用comparator。如果arraylist中的对象实现可比性,则可以这样做:

1
Collections.sort(myArrayList);

collections类包含许多有用的、通用的工具。


Java 8 lambda表达式

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

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


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

1
2
3
import static java.util.Comparator.comparing;

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


因为技术每天都会出现,答案会随着时间的推移而改变。我看了一眼兰姆达吉,看起来很有趣。

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

这里有一个例子:

排序迭代

1
2
3
4
5
6
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排序

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

当然,在性能上有这种美的影响(平均是2倍),但是你能找到一个更可读的代码吗?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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());
    }

}

}


Java 8的最简单的方法是英语字母排序。

类实现

1
2
3
4
5
6
7
8
public class NewspaperClass implements Comparable<NewspaperClass>{
   public String name;

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

排序

1
  Collections.sort(Your List);

如果要对包含非英语字符的字母表进行排序,可以使用区域设置…下面的代码使用土耳其字符排序…

类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
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);
   }
}

排序

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


函数和方法引用

Collections.sort方法可以使用您传递的ComparatorList进行排序。可以使用Comparator.comparing方法来实现Comparator,在这种方法中,您可以传递一个方法引用作为必要的Function方法。幸运的是,实际的代码比这个描述简单得多,而且短得多。

对于Java 8:

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

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

另一种方式是

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


Java 8起,我们不必直接使用Collections.sort()List接口有默认的sort()方法:

1
2
3
List<User> users = Arrays.asList(user1,user2,user3);
users.sort( (u1, u2) -> {
return u1.getFirstName.compareTo(u2.getFirstName());});

请参阅http://visvv.blogspot.in/2016/01/sorting-objects-in-java-8.html。


可以使用bean comparator对自定义类中的任何属性进行排序。


Java 8 lambda缩短了排序。

1
Collections.sort(stdList, (o1, o2) -> o1.getName().compareTo(o2.getName()));


是的,这是可能的,例如,在这个答案中,我按照IndexValue类的v属性排序。

1
2
3
4
5
6
    // Sorting by property v using a custom comparator.
    Arrays.sort( array, new Comparator<IndexValue>(){
        public int compare( IndexValue a, IndexValue b ){
            return a.v - b.v;
        }
    });

如果您注意到这里,我正在创建一个匿名的内部类(它是用于闭包的Java),并将其直接传递给类EDCOX1(15)的方法EDCOX1(16)。

您的对象还可以实现EDCOX1的"1"(即Java中的字符串和大多数核心库),但它将定义它自己类的"自然排序",并且不允许插入新的排序顺序。


可以使用Java 8进行排序。

1
2
3
4
5
yourList.sort(Comparator.comparing(Classname::getName));

or

yourList.stream().forEach(a -> a.getBObjects().sort(Comparator.comparing(Classname::getValue)));

您可以尝试番石榴点菜:

1
2
3
4
5
6
7
8
Function<Item, Date> getStartDate = new Function<Item, Date>() {
    public Date apply(Item item) {
        return item.getStartDate();
    }
};

List<Item> orderedItems = Ordering.natural().onResultOf(getStartDate).
                          sortedCopy(items);

我发现,如果不是所有的答案都依赖于底层类(对象)来实现Comparable,或者拥有一个与助手类似的接口。

不是我的解决方案!下面的代码允许您通过知道对象的字符串名称来比较对象的字段。您可以很容易地修改它,不使用这个名称,但是您需要公开它或构造一个要与之比较的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
Collections.sort(anArrayListOfSomeObjectPerhapsUsersOrSomething, new ReflectiveComparator(). new ListComparator("name"));

public class ReflectiveComparator {
    public class FieldComparator implements Comparator<Object> {
        private String fieldName;

        public FieldComparator(String fieldName){
            this.fieldName = fieldName;
        }

        @SuppressWarnings({"unchecked","rawtypes" })
        @Override
        public int compare(Object object1, Object object2) {
            try {
                Field field = object1.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);

                Comparable object1FieldValue = (Comparable) field.get(object1);
                Comparable object2FieldValue = (Comparable) field.get(object2);

                return object1FieldValue.compareTo(object2FieldValue);
            }catch (Exception e){}

            return 0;
        }
    }

    public class ListComparator implements Comparator<Object> {
        private String fieldName;

        public ListComparator(String fieldName) {
            this.fieldName = fieldName;
        }

        @SuppressWarnings({"unchecked","rawtypes" })
        @Override
        public int compare(Object object1, Object object2) {
            try {
                Field field = object1.getClass().getDeclaredField(fieldName);
                field.setAccessible(true);
                Comparable o1FieldValue = (Comparable) field.get(object1);
                Comparable o2FieldValue = (Comparable) field.get(object2);

                if (o1FieldValue == null){ return -1;}
                if (o2FieldValue == null){ return 1;}
                return o1FieldValue.compareTo(o2FieldValue);
            } catch (NoSuchFieldException e) {
                throw new IllegalStateException("Field doesn't exist", e);
            } catch (IllegalAccessException e) {
                throw new IllegalStateException("Field inaccessible", e);
            }
        }
    }
}

此代码段可能很有用。如果要对对象排序在我的示例中,我希望按卷名排序:

1
2
3
4
5
6
7
8
9
10
11
12
public List<Volume> getSortedVolumes() throws SystemException {
    List<Volume> volumes = VolumeLocalServiceUtil.getAllVolumes();
    Collections.sort(volumes, new Comparator<Volume>() {
        public int compare(Volume o1, Volume o2) {
            Volume p1 = (Volume) o1;
            Volume p2 = (Volume) o2;
            return p1.getVolumeName().compareToIgnoreCase(
                    p2.getVolumeName());
        }
    });
    return volumes;
}

这是可行的。我在JSP中使用它。


您可以在2016德国斯图加特的JAVA论坛上查看这个演示文稿。

只有少数幻灯片使用德语,99%的内容是"基于英语"的Java源代码;

1
2
3
4
5
someCollection.sort(
  OurCustomComparator
    .comparing(Person::getName)
    .thenComparing(Person::getId)
);

其中OurCustomComparator使用默认方法(以及其他有趣的想法)。如图所示,导致非常简洁的代码选择一些getter方法进行排序;以及排序条件的超简单链接(或反转)。

如果你对Java8感兴趣,你会在那里找到很多材料让你开始学习。


自1.8以来新增的是list.sort()方法,而不是使用collection.sort()。所以直接调用myListContainer.sort()。

下面是演示list.sort()功能的代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<Fruit> fruits = new ArrayList<Fruit>();
fruits.add(new Fruit("Kiwi","green",40));
fruits.add(new Fruit("Banana","yellow",100));
fruits.add(new Fruit("Apple","mixed green,red",120));
fruits.add(new Fruit("Cherry","red",10));

// a) using an existing compareto() method
fruits.sort((Fruit f1,Fruit f2) -> f1.getFruitName().compareTo(f2.getFruitName()));
System.out.println("Using String.compareTo():" + fruits);
//Using String.compareTo(): [Apple is: mixed green,red, Banana is: yellow, Cherry is: red, Kiwi is: green]

// b) Using a comparable class
fruits.sort((Fruit f1,Fruit f2) -> f1.compareTo(f2));  
System.out.println("Using a Comparable Fruit class (sort by color):" + fruits);
// Using a Comparable Fruit class (sort by color): [Kiwi is green, Apple is: mixed green,red, Cherry is: red, Banana is: yellow]

水果类为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Fruit implements Comparable<Fruit>
{
    private String name;
    private String color;
    private int quantity;

    public Fruit(String name,String color,int quantity)
    { this.name = name; this.color = color; this.quantity = quantity; }

    public String getFruitName() { return name; }        
    public String getColor() { return color; }  
    public int getQuantity() { return quantity; }

    @Override public final int compareTo(Fruit f) // sorting the color
    {
        return this.color.compareTo(f.color);
    }    
    @Override public String toString()
    {  
        return (name +" is:" + color);
    }
} // end of Fruit class

使用此库,可以对多列上的自定义对象列表进行排序。该库使用8.0版功能。那里也有样品。这是一个样品

1
2
3
4
5
6
7
8
9
10
11
12
13
SortKeys sortKeys = new SortKeys();
sortKeys.addField("firstName")
            .addField("age", true); // This (true) will sort the age descending

// Other ways to specify a property to the sorter are
//      .addField("lastName", String.class);
//      .addField("dob", Date.class, true);

// Instantiate a ListSorter
ListSorter listSorter = new ListSorter();

// Pass the data to sort (listToSort) and the"by keys" to sort (sortKeys)
List sortedList = (List<Person>) listSorter.sortList(listToSort, sortKeys);

CustomComparator类必须实现java.util.Comparator才能使用。它还必须覆盖compare()和equals()。

compare()必须回答以下问题:对象1是小于、等于还是大于对象2?

完整文档:http://java.sun.com/j2se/1.5.0/docs/api/java/util/comparator.html


我更喜欢这个过程:

1
2
3
4
5
6
7
8
9
10
public class SortUtil
{    
    public static <T> List<T> sort(List<T> list, String sortByProperty)
    {
            Collections.sort(list, new BeanComparator(sortByProperty));
            return list;
    }
}

List<T> sortedList = SortUtil<T>.sort(unsortedList,"startDate");

如果对象列表中有一个名为startDate的属性,则会多次调用use this。你甚至可以把它们连在一起。

这就要求你的目标是Comparable,也就是说你需要compareToequalshashCode的实现。

是的,可能会更快…但是现在您不必为每种类型制作一个新的比较器。如果您可以节省开发时间并放弃运行时,那么您可以使用这个方法。


自定义类可以实现"Comparable"接口,这需要实现CompareTo方法。在CompareTo方法中,您可以定义一个对象小于或大于另一个对象的含义。所以在您的示例中,它可以看起来像这样:

1
public class MyCustomClass implements Comparable<MyCustomClass>{

……

1
2
3
4
5
6
7
8
9
10
 @Override
public int compareTo(MyCustomClass a) {
    if(this.getStartDate().before(a.getStartDate())){
        return -1;
    }else if(a.getStartDate().before(this.getStartDate())){
        return 1;
    }else {
        return 0;
    }
}

负数表示此值小于要比较的对象。正数表示大于"与对象比较",零表示对象相等。

然后,您可以使用collections.sort(mylist)对列表进行排序,而无需输入比较器。如果使用像treeset或treemap这样的已排序收集数据结构,此方法还具有自动排序的优点。

如果您想了解更多关于可比较接口的信息(公开:我是作者;)可以查看本文。https://nullbeans.com/the-java-comparable-interface-automatic-sort-of-collections/


使用JAVA 8使用EDCOX1,12定义,可以在一行中定义EDCOX1×2

使用以下任何一种方法:

选项1:

1
listToBeSorted.sort(Comparator.comparing(CustomObject::getStartDate));

选项2:

1
Collections.sort(listToBeSorted, Comparator.comparing(CustomObject::getStartDate));