使用Java Comparator根据条件使用多个字段进行排序

时间:2017-01-04 01:54:29

标签: java sorting collections filter comparator

我对有效使用Java Comparator有疑问。

class MyClass {
    //Active State: OPEN, PENDING, RUNNING
    private String state;
    private Date startDate;
    private Date endDate;
}

此处state字段的可能值为OPEN,PENDING,RUNNING,CLOSED,CANCELLED等,其中OPEN,PENDING和RUNNING为活动状态。现在我想编写比较器来对List<MyClass>进行排序,以便活动的那些首先出现并按startDate排序,然后是基于endDate排序的非活动的。

static final Set<String> ACTIVE;// this set contains OPEN, PENDING, RUNNING

List<MyClass> myList;//This is my list
...
Collections.sort(myList, new Comparator<MyClass>() {
    @Override
    public int compare(MyClass o1, MyClass o2) {
        int c;
        boolean isO2 = ACTIVE.contains(o2.getState());
        boolean isO1 = ACTIVE.contains(o1.getState());
        if (isO2 && isO1) {
            c = DateTimeComparator.getInstance().compare(o2.getStartDate(), o1.getStartDate());
        } else if (isO2) {
            c = 1;
        } else if (isO1) {
            c = -1;
        } else {
            c = DateTimeComparator.getInstance().compare(o2.getEndDate(), o1.getEndDate());
        }
        return c;
    }
});

我的问题是我的上面实施的单比较器是否良好?或者有更好的方法吗? 我很可能不得不坚持使用Java 7,但也欢迎使用Java 8的解决方案。

1 个答案:

答案 0 :(得分:1)

在Java 8中,我认为使用Comparator::comparing会更清晰一点。例如:

Comparator<MyClass> comparator = Comparator.nullsFirst(Comparator.comparing((MyClass myClass) -> !isActive(myClass))
    .thenComparing((MyClass myClass) -> isActive(myClass) ? myClass.startDate : myClass.endDate, Comparator.nullsFirst(DateTimeComparator.getInstance())));

private static boolean isActive(MyClass myClass)
{
    switch (myClass.state)
    {
    case "OPEN":
    case "PENDING":
    case "RUNNING":
        return true;
    default:
        return false;
    }
}

在Java 7中,假设您在类路径上有Guava,则可以使用Ordering。例如:

Comparator<MyClass> comparator = Ordering.natural().reverse().onResultOf(new Function<MyClass, Boolean>() {
        @Override
        public Boolean apply(MyClass myClass) {
            return isActive(myClass);
        }
    })
    .compound(Ordering.from(DateTimeComparator.getInstance()).nullsFirst().onResultOf(new Function<MyClass, Date>() {
        @Override
        public Date apply(MyClass myClass) {
            return isActive(myClass) ? myClass.startDate : myClass.endDate;
        }
    }))
    .nullsFirst();