简体   繁体   中英

Sorting list of objects by date property

I have an object that contains two LocalDate properties:

public class SomeObject {
    private LocalDate startDate;
    private LocalDate endDate;
}

Constructor and stuff ommitted for brevity. I want to sort a list of these objects by their startdate and then assign the startdate of the next object to the previous object's enddate. To clarify, I start with a list of these objects:

SomeObject object1 = new SomeObject(LocalDate.parse("2015-01-01"), null);
SomeObject object2 = new SomeObject(LocalDate.parse("2014-01-01"), null);
SomeObject object3 = new SomeObject(LocalDate.parse("2016-01-01"), null);

List<SomeObject> list = Arrays.asList(object1, object2, object3);

And after sorting it should return this:

for (SomeObject object : list) {
    System.out.println(object.startDate.toString() + " " + object.endDate.toString() );
}

2014-01-01 2015-01-01
2015-01-01 2016-01-01
2016-01-01 null

Each list will only contain 3 or 4 of these objects at most, but the code might have to process tens of thousands of these lists, so I'm looking for an efficient way to do this.

You can use Collections.sort with a Comparator. In Java 8 with Lambdas it looks like this:

    Collections.sort(list, (x, y) -> x.startDate.compareTo(y.startDate));

    for (int i = 0; i < (list.size() - 1); i++) {
        list.get(i).endDate = list.get(i + 1).startDate;
    }

作为对已接受答案的增强:

Collections.sort(list, Comparator.comparing(SomeObject::getStartDate));

Make use of the fact that LocalDate already implements Comparable and make your SomeObject do as well. Additionally, give it a proper toString() method, which handles null values in order to represent your object as a String :

public class SomeObject implements Comparable<SomeObject> {
    private LocalDate startDate;
    private LocalDate endDate;

    public SomeObject(LocalDate startDate, LocalDate endDate) {
        this.startDate = startDate;
        this.endDate = endDate;
    }

    @Override
    public int compareTo(SomeObject anotherObject) {
        return this.startDate.compareTo(anotherObject.startDate);
    }

    @Override
    public String toString() {
        String start = startDate == null ? "null" : startDate.format(DateTimeFormatter.ISO_LOCAL_DATE);
        String end = endDate == null ? "null" : endDate.format(DateTimeFormatter.ISO_LOCAL_DATE); 
        StringBuilder sb = new StringBuilder();
        sb.append(start).append(" ").append(end);
        return sb.toString();
    }
}

By doing so, you can easily just call Collections.sort(list); and have your data sorted by startDate :

public class SomeObjectSorting {

    public static void main(String[] args) {
        SomeObject object1 = new SomeObject(LocalDate.parse("2015-01-01"), null);
        SomeObject object2 = new SomeObject(LocalDate.parse("2014-01-01"), LocalDate.parse("2017-01-01"));
        SomeObject object3 = new SomeObject(LocalDate.parse("2016-01-01"), null);

        List<SomeObject> list = Arrays.asList(object1, object2, object3);

        System.out.println("———— BEFORE SORTING ————");

        list.forEach(object -> {
            System.out.println(object.toString());
        });

        Collections.sort(list);

        System.out.println("———— AFTER SORTING ————");

        list.forEach(object -> {
            System.out.println(object.toString());
        });
    }
}

As you mentioned that you didn't really care whether it is startDate or endDate and just order all of them, maybe the following will help you:

List<LocalDate> dates = list.stream()
        .flatMap(s -> Stream.of(s.startDate, s.endDate))
        .filter(Objects::nonNull) // maybe... if nulls are required too, then skip that part here... (but also check the sorting variant then); note that I use null now if the last date is reached (check the printing part for that)
        .distinct()
        .sorted()                 // natural order
        // alternatively: natural order + nulls last
        // .sorted(Comparator.nullsLast(Comparator.comparing(Function.identity())))
        .collect(Collectors.toList());

// printing part:
IntStream.range(0, dates.size())
        .mapToObj(i -> {
            String from = Objects.toString(dates.get(i));
            String upto = Objects.toString(i < dates.size() - 1 ? dates.get(i + 1) : null); // exchange null with the end date you are expecting
            return from + " - " + upto;
        })
        .forEach(System.out::println);

EDIT: There was that endDate set on one of your samples before... as that isn't the case anymore, here an update how you can set the right date ranges. It's basically similar to what also Ralf Renz has used in his answer:

list.sort(Comparator.comparing(SomeObject::getStartDate));
IntStream.range(0, list.size() - 1)
         .forEach(i -> list.get(i).endDate = list.get(i + 1).startDate);
// or if you care about performance, just do the same as Ralf did:
for (int i = 0; i < (list.size() - 1); i++) {
    list.get(i).endDate = list.get(i + 1).startDate;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM