3

I have a Map<Element, Attributes> consisting of instances of the following (example) class and enum, where I want to get the value of the most recent key via stream(). The most recent key can be determined by the property creationTime of the class Element and the corresponding value in the Map is just an enum value:

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class Element implements Comparable<Element> {

    String abbreviation;
    LocalDateTime creationTime;

    public Element(String abbreviation, LocalDateTime creationTime) {
        this.abbreviation = abbreviation;
        this.creationTime = creationTime;
    }

    public String getAbbreviation() {
        return abbreviation;
    }

    public void setAbbreviation(String abbreviation) {
        this.abbreviation = abbreviation;
    }

    public LocalDateTime getCreationTime() {
        return creationTime;
    }

    public void setCreationTime(LocalDateTime creationTime) {
        this.creationTime = creationTime;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    @Override
    public int compareTo(Element otherElement) {
        return this.creationTime.compareTo(otherElement.getCreationTime());
    }

    @Override
    public String toString() {
        return "[" + abbreviation + ", " + creationTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + "]";
    }
}

Please not that an Element implements Comparable<Element> just using the built-in comparison of LocalDateTime.

public enum Attributes {

    DONE,
    FIRST_REGISTRATION,
    SUBSEQUENT_REGISTRATION
}

My current approach is just able to filter the keySet and find the most recent key, which I then use to simply get the value in a new line of code. I was wondering if it is possible in a single stream().filter(...) statement:

Map<Element, Attributes> results = new TreeMap<>();

// filling the map with random elements and attributes

Element latestKey = results.keySet().stream().max(Element::compareTo).get();
Attributes latestValue = results.get(latestKey);

Can we get a value by filtering the keySet of a Map in a single stream() statement like

Attributes latestValue = results.keySet().stream()
                .max(Element::compareTo)
                // what can I use here?
                .somehowAccessTheValueOfMaxKey()
                .get()

?

Additional information I do not need a default value like null, because the Map will only be checked if it contains at least one key-value pair, which means there will always be a most recent element-attribute pair, a single one, at least.

deHaar
  • 17,687
  • 10
  • 38
  • 51
  • 2
    I am a bit concerned at the fact that your class doesn't contain an implementation for `equals` and `hashCode`, which may cause the map not to work at all. Also, consider if using `LinkedHashMap` will not save you the need for using this class as a key. If the time stamps merely represent the order of insertion, it may be an easy replacement. – RealSkeptic Feb 20 '19 at 09:59
  • I omitted the `equals()` method in this example, my original class has one (generated by eclipse). Is anything else wrong with the use of `TreeMap`? – deHaar Feb 20 '19 at 10:01

3 Answers3

5

You can find the max Entry instead of the max key:

Attributes latestValue =
    results.entrySet()
           .stream()
           .max(Comparator.comparing(Map.Entry::getKey))
           .map(Map.Entry::getValue)
           .get();
Eran
  • 387,369
  • 54
  • 702
  • 768
  • I currently prefer the solution provided by @suit because it is less code and has no `.orElse(null)`. I don't need it, which I have edited into the question as additional information at the bottom. – deHaar Feb 20 '19 at 10:06
  • 2
    @deHaar you can replace the orElse(null) with get() if the Map cannot be empty. The advantage of my solution is that it doesn't require an additional Map.get operation. Even though Map.get takes expected time of O(1), it is still faster to obtain the value directly from the Entry instead of searching the Map for the value matching the key. – Eran Feb 20 '19 at 10:08
  • Thanks for the explanation. I will have to think about which one is the best answer, they are all good ;-) – deHaar Feb 20 '19 at 10:20
5
Attributes latestValue = results.keySet().stream()
            .max(Element::compareTo)
            .map(results::get)
            .get()
Lino
  • 19,604
  • 6
  • 47
  • 65
suit
  • 569
  • 2
  • 13
  • Even less code than in the answer given by @Eran, cool! Thanks, it also seems to get the desired value. – deHaar Feb 20 '19 at 09:59
1

You can also use Collectors.toMap with TreeMap as a map factory

Attributes value = results.entrySet().stream()
       .collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (v1, v2) -> v1, TreeMap::new))
       .lastEntry().getValue();
Ruslan
  • 6,090
  • 1
  • 21
  • 36