Zed
Zed

Reputation: 5921

Retrieving multiple min occurrences from a list

I have a list of custom objects:

List<CustomObject> customObjects;

from which I would like to extract all the objects that have the have the earliest datetime value set.

So the class would look something like this:

class CustomObject {
   LocalDateTime date;

   public LocalDateTime getDateTime() {
       return date;
   }
}

and I'm able to successfully find the object in the list with the earliest date with a custom comparator function like this:

private static LocalDateTime getDate(CustomObject customObject) {
        return customObject.getDateTime();
}

CustomObject customObjectMin = customObjects.stream().
           min(Comparator.comparing(MyUtilClass::getDate));

However, it is possible to have multiple custom objects with the same date, but it looks like there is no way to get multiple occurrences in that scenario with the min. Is there an easy solution to finding all the objects in the list with the earliest date set ? Something like this:

List<CustomObject> customObjectsMin = customObjects.stream().
           minWithAllOccurences(Comparator.comparing(MyUtilClass::getDate));

Upvotes: 3

Views: 409

Answers (2)

Patrick Parker
Patrick Parker

Reputation: 4959

Other than Peter Lawrey's excellent answer, I would like to point out that it is possible to do this with a single stream while avoiding the memory costs of collecting every element into a TreeMap. How? One way would be to use reduce(), as follows:

List<SampleJava> customObjectsMin = customObjects.stream()
        .reduce(new ArrayList<>(), // identity
        (List<SampleJava> list, SampleJava item) -> { // accumulate
            if(list.isEmpty() ||  getDate(item).compareTo(getDate(list.get(0))) < 0) {
                return new ArrayList<>(Arrays.asList(item));
            } else if(getDate(item).equals(getDate(list.get(0)))) {
                list.add(item);
            }
            return list;
        },
        (list1, list2) -> { // combine
            if(list1.isEmpty()) return list2; 
            if(list2.isEmpty()) return list1; 
            int cmp = getDate(list1.get(0)).compareTo(getDate(list2.get(0)));
            if(cmp < 0) return list1;
            if(cmp > 0) return list2;
            list1.addAll(list2);
            return list1;
        });

Upvotes: 0

Peter Lawrey
Peter Lawrey

Reputation: 533640

You can do two selections.

  • one to find the min date
  • one to find those with that date

e.g.

LocalDate min = customObjects.stream()
                             .map(CustomObject::getDateTime)
                             .min(Comparator.naturalOrder());
List<CustomObject> objs = customObjects.stream()
                             .filter(c -> min.equals(c.getDateTime()))
                             .collect(Collectors.toList());

Or you can use Collectors.groupingBy into a TreeMap and take the first entry.

Upvotes: 3

Related Questions