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


1145

我读过有关使用Comparator对ArrayList进行排序的信息,但在所有示例中,人们都在使用 compareTo,根据一些研究都是String的一种方法。

我想按自定义对象的属性之一对ArrayList进行排序:Date对象(getStartDay())。通常,我将它们进行比较,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);
    ...
}


2
@Yishai的回答在本文中演示了如何使用比较器链接将枚举优雅地用于自定义排序和分组排序(多个参数)。
gunalmel 2012年

Answers:


1535

自从Date实现以来Comparable,它就有一个compareTo像方法一样的方法String

因此,您的自定义设置应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()));

这是一个非常常见的习惯用法,因此有一个内置方法可以Comparator为带有Comparable键的类生成一个:

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

所有这些都是等效形式。


33
+1表示它应该返回int,因此您最好使用Date#compareTo()它。为什么不能超越其他答案,这超出了我的范围。该链接也可能有用:Sun.com上的对象订购教程
BalusC,2010年

5
我认为最好的答案还应该包括在Java 8中执行该操作的正确方法。读起来更好,更不容易出错。编写return o1.getStartDate()。compareTo(o1.getStartDate())非常容易。
库巴2014年

2
比较器类应为静态:)
Jarmez De La Rocha 2014年

4
@Kuba更好,使用List.sort()
shmosel '16

3
此解决方案不适用于Android API <24。你们知道解决方案吗?
Jim Clermonts

194

具有自然排序顺序的类(例如,一个类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时,我无法访问该类(当然,因为它不是公共的,所以没有)。我是否需要在单独的文件中创建一个新的公共ChairWeightComparator类?-我真的是3年后第一个尝试过这种方法的人吗?还是我错过了某件事?
user387184 2014年

@ user387184-只需将其公开,并将其放在自己的文件中(最好也是自己的包中),您就可以在项目中的任何地方使用它。无需创建其他类!
比约恩2014年

您的意思是创建一个新文件-不是一个类,然后输入代码:“类ChairWeightComparator实现Comparator <Chair> {....”?
user387184 2014年

@ user387184,完全是-但关键字public在前面class
比约恩2014年

157

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

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


这样可以排序,但每个元素都具有双重价值
Sam Sam

44

是的你可以。比较项目有两个选项,可比较界面和比较器界面。

这两个接口都允许不同的行为。Comparable允许您使对象像刚刚描述的String一样工作(实际上,String实现Comparable)。第二个是Comparator,它允许您执行要求的操作。您可以这样做:

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

这将导致Collections.sort方法将比较器用于其排序机制。如果ArrayList中的对象实现可比较,则可以改为执行以下操作:

Collections.sort(myArrayList);

集合类包含了一些这些有用的,常用工具。


42

JAVA 8 Lambda表达式

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

要么

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

3
Collections.sort(studList, Comparator.comparing(Student::getFirstName));
Holger

5
..或studList.sort(Comparator.comparing(Student::getFirstName));
Alexis C.

在您的情况下,排序顺序将一直递增。在我的示例中,我也照顾到排序顺序。谢谢先生们。
Sorter

33

使用Java 8时,可以将方法引用用作比较器:

import static java.util.Comparator.comparing;

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

@ user387184不幸的是,android不支持Java 8,尽管可能存在解决方法(我尚未测试过)。
assylias 2014年

14

由于技术每天都会出现,答案会随着时间而改变。我看了看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倍),但是您能找到更具可读性的代码吗?


这样排序,但为每个元素提供双值值,如何避免它
Sam Sam

@Sam,它不应该...按预期工作。除非您使用的是带有错误的新版本,否则建议您将其发布到论坛中。无论如何,这个答案是在Java 8之前发布的,如果您使用它,那么它将比使用lambdaj更好
Federico Piazza

否则,我不得不在foreach循环中甚至删除项目,否则它给了我每个内容的两倍。
山姆

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

}

}

7
欢迎使用stackoverflow。这个问题已经在前一段时间回答了。在恢复旧线程之前,请确保您的响应为该线程添加了重要内容。

1
请在回答中添加解释。
Arashsoft '16

只是编译并运行。该代码是注释和解释。
CharlesW

9

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

如果要对包含非英文字符的字母进行排序,可以使用“语言环境...”下面的代码使用“土耳其语字符排序” ...

类实施

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

9

功能与方法参考

Collections.sort方法可以List使用“ Comparator通过” 对a 进行排序。这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,


6

Java 8 Lambda缩短了排序时间。

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

2
Collections.sort(stdList, Comparator.comparing(SomeClass::getName));
bcsb1001 2015年


5

是的,这是可能的,例如在此答案中,我按v类的属性进行排序IndexValue

    // 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),并将其直接传递给sort该类的方法Arrays

您的对象也可以实现Comparable(这是String和Java中大多数核心库所做的事情),但是它将定义它自己的类的“自然排序顺序”,并且不允许您插入新的类。


1
...但是您可以使用Comparator:) 覆盖
BalusC,2010年

5

我发现大多数(如果不是全部)这些答案都依赖于基础类(对象)来实现可比或具有辅助可比接口。

没有我的解决方案!以下代码可让您通过了解对象的字符串名称来比较对象的字段。您可以轻松地修改它而不使用名称,但是随后您需要公开它或构造要与之比较的对象之一。

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

5

您可以尝试番石榴订购

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

5

您可以使用Java 8进行排序

yourList.sort(Comparator.comparing(Classname::getName));

or

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

3

此代码段可能有用。如果要对我的对象进行排序,我想按VolumeName进行排序:

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中使用它。


3

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

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

3

您可以看一下此演示文稿在2016年德国斯图加特的Java论坛上。

只有少数几张幻灯片使用德语,其中99%的内容是“基于英语的” Java源代码。喜欢

someCollection.sort(
  OurCustomComparator
    .comparing(Person::getName)
    .thenComparing(Person::getId)
);

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

如果您对java8感兴趣,那么您会发现很多入门知识。


3

自1.8版以来的新功能是List.sort()方法,而不是使用Collection.sort(),因此您可以直接调用mylistcontainer.sort()

这是演示List.sort()功能的代码片段:

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]

水果类是:

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   


1

我更喜欢这个过程:

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,则一遍又一遍地使用它。您甚至可以链接它们startDate.time

这需要你的对象是Comparable你需要哪些手段compareToequals以及hashCode实现。

是的,它可能会更快...但是现在您不必为每种类型都创建新的Comparator。如果您可以节省开发时间并放弃运行时,则可以使用此工具。


3
1,这个答案是2小时前给出的,同时提供了工作代码。不需要重新发布相同的解决方案并使论坛变得混乱,尤其是因为BeanComparator不是标准类,因此,如果发布者不知道您在说什么,它就不是真正的解决方案。如果您喜欢原始建议,则可以对其进行投票,并根据需要添加评论。
camickr 2010年

0

使用Java 8使用可以使用以下命令定义Comparator一行Comparator.comparing()

使用以下任何一种方式:

选项1:

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

选项2:

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

1
似乎是该答案在3年前的副本。
罗勒·布尔克

1
不完全是,这里只有选项2与上述答案中的选项1相似。我认为,如果两个答案都回答同一问题,那么它们至少会有一些相似之处。
萨希尔·查布拉

0

您的自定义类可以实现“ Comparable”接口,该接口需要执行CompareTo方法。然后,在CompareTo方法中,可以定义一个对象小于或大于另一个对象的含义。因此,在您的示例中,它看起来可能像这样:

public class MyCustomClass implements Comparable<MyCustomClass>{

.....

 @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之类的排序集合数据结构,此方法还具有使事物自动排序的优点。

如果您想了解有关Comparable接口的更多信息,可以查看本文(披露:我是作者;)) https://nullbeans.com/the-java-comparable-interface-automatic-sort-of-collections/


0

如果您只有要排序的(嵌套)属性的String属性路径,则也可以使用Springs PropertyComparator

List<SomeObject> list = ...;
PropertyComparator<HitWithInfo> propertyComparator = new PropertyComparator<>(
    "property.nested.myProperty", false, true);
list.sort(propertyComparator);

缺点是,该比较器无声地忽略了不存在或不可访问的属性,并将其作为值进行比较。这意味着,您应该仔细测试这样的比较器或以某种方式验证属性路径的存在。


0

使用java-8流api可以对以下项ArrayList进行排序:

 Comparator<Person> birthdayComparator = Comparator.comparing(Person::getBirthday);
 List<Person> sortedList = list.stream().sorted(birthdayComparator).collect(toList());

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.