4

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.

Spacejockey
  • 370
  • 1
  • 6
  • 18
  • 2
    Somehow the described output doesn't match the objects added in the code snippet above. There are two objects with `endDate=null` added but in the desired output there is only one without `endDate` – dpr Nov 29 '18 at 14:27
  • @dpr The initial end dates don't matter, they need to be replaced by the start dates of the next object in the sorted list. – Spacejockey Nov 29 '18 at 14:30
  • do you know how to use a comparator? I can provide example of using a comparator to sort date list if you'd like. – Artanis Nov 29 '18 at 14:32
  • 1
    I've edited the starting values to hopefully make it more clear. – Spacejockey Nov 29 '18 at 14:36
  • Possible duplicate of [Sort objects in ArrayList by date?](https://stackoverflow.com/questions/5927109/sort-objects-in-arraylist-by-date) and other questions — use your search engine. – Ole V.V. Nov 29 '18 at 14:45
  • I’d use `list.sort(Comparator.comparing(SomeObject::getStartDate));` (assuming you have a getter by that name — which might be a good idea anyway). – Ole V.V. Nov 29 '18 at 14:48
  • do you want to set the `endDate` to the nearest possible `startDate` or is it just that you want to output all consecutive date ranges in order? – Roland Nov 29 '18 at 15:20

4 Answers4

8

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;
    }
Ralf Renz
  • 1,061
  • 5
  • 7
  • 2
    Even better: `Collections.sort(list, Comparator.comparing(obj -> obj.startDate));`. – Ole V.V. Nov 30 '18 at 18:23
  • 1
    why all of you use `Collections.sort` instead of `list.sort(Comparator.comparing(SomeObject::getStartDate))` (assuming `getStartDate`-accessor)? – Roland Dec 11 '18 at 15:11
  • 1
    list.sort was only introduced in Java 1.8. Collections.sort is older and better known. Besides that it makes no difference, I think. – Ralf Renz Dec 12 '18 at 09:03
5

As an enhancement to the accepted answer:

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

Amir Dora.
  • 2,831
  • 4
  • 40
  • 61
Morteza
  • 642
  • 7
  • 17
1

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;
}
Roland
  • 22,259
  • 4
  • 57
  • 84
0

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());
        });
    }
}
deHaar
  • 17,687
  • 10
  • 38
  • 51