0

I have Documents objects which I want to group by document ID. After grouping them, I want to get their "maximum". This is what I have so far:

List<Document> docList = getDocuments(...);
Map<Long, Document> docIdsToLatestDocVersions = docList.stream()
     .collect(Collectors.groupingBy(
           Document::getDocumentId,
           Collectors.reducing(BinaryOperator.maxBy(Comparator.comparing( Function.identity() ))
     ));

The Document class:

class Document {
    int documentId;
    int majorVersion;
    int minorVersion;

    @Override
    public int compareTo(Document document) {
        return new CompareToBuilder()
            .append(this.getDocumentId(), document.getDocumentId())
            .append(this.getMajorVersion(), document.getMajorVersion())
            .append(this.getMinorVersion(), document.getMinorVersion())
            .toComparison();
    }
}

Importantly, I already have a compareTo function implemented. I'm not sure what to put in my reducer argument of the groupingBy clause. I also tried:

Map<Long, Document> docIdsToLatestDocVersions = docList.stream()
    .collect(Collectors.toMap(
          Document::getDocumentId, Function.identity(),
          BinaryOperator.maxBy(Comparator.comparing(d -> d))));

but to no avail.

E-Riz
  • 31,431
  • 9
  • 97
  • 134
  • Does this answer your question? [Java Streams – How to group by value and find min and max value of each group?](https://stackoverflow.com/questions/51377851/java-streams-how-to-group-by-value-and-find-min-and-max-value-of-each-group) – Rishal Aug 12 '22 at 15:55
  • I came across that forum, but I couldn't figure out what to use inside `Comparator.comparing( ... )`. I guess I'm just inexperienced with streams/Collectors. – Steven Xiong Aug 12 '22 at 20:50

1 Answers1

3

In your first snippet Collectors.reducing will return an Optional, you either need to change the type of your result map to Map<Integer, Optional<Document>> or wrap the reducing collector in to a collectingAndThen collector to unwrap the optional:

Map<Integer, Document> docIdsToLatestDocVersions =
     docList.stream()
            .collect(Collectors.groupingBy(Document::getDocumentId,
                                           Collectors.collectingAndThen(
                                                   Collectors.reducing(BinaryOperator.maxBy(Document::compareTo)),
                                                   Optional::get)));

But as you already mentioned and already tried, whenever you are confronted with Optionals after grouping, it is a good idea to check if Collectors.toMap is the better choise. And in this case it is indeed way more readable:

Map<Integer, Document> docIdsToLatestDocVersions_second =
     docList.stream()
            .collect(Collectors.toMap(Document::getDocumentId,
                                      Function.identity(),
                                      BinaryOperator.maxBy(Document::compareTo)));
Eritrean
  • 15,851
  • 3
  • 22
  • 28