Using Java 8 stream methods to get the last max value

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP












16















Given a list of items with properties, I am trying to get the last item to appear with a maximum value of said property.



For example, for the following list of objects:



t i
A: 3
D: 7 *
F: 4
C: 5
X: 7 *
M: 6


I can get one of the Things with the highest i:



Thing t = items.stream()
.max(Comparator.comparingLong(Thing::getI))
.orElse(null);


However, this will get me Thing t = D. Is there a clean and elegant way of getting the last item, i.e. X in this case?



One possible solution is using the reduce function. However, the property is calculated on the fly and it would look more like:



Thing t = items.stream()
.reduce((left, right) ->
long leftValue = valueFunction.apply(left);
long rightValue = valueFunction.apply(right);
return leftValue > rightValue ? left : right;
)
.orElse(null);


The valueFunction now needs to be called nearly twice as often.



Other obvious roundabout solutions are:



  1. Store the object in a Tuple with its index

  2. Store the object in a Tuple with its computed value

  3. Reverse the list beforehand

  4. Don't use Streams









share|improve this question



















  • 1





    "The valueFunction now needs to be called nearly twice as often." Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D. How about you just cache the calculated value directly in the Thing instance?

    – tobias_k
    Feb 5 at 16:14












  • @tobias_k I realised that after posting my question. My practical solution was to use a standard for each.

    – Druckles
    Feb 5 at 17:35






  • 2





    If your stream goes parallel or async, there can be a complete loss of the idea of "in order". Be sure to think about this and consider if your stream can contain a field or value indicating the order, similar to a tuple of (value, orderIndex) so that the max operation is a simple, parallelizable comparison of the value and orderIndex fields.

    – ErikE
    Feb 5 at 19:19











  • @ErikE if the stream has a defined encounter order, the reduce based solution will work in parallel, as long as the reduction function fulfills the associativity constraint. Which is the case here.

    – Holger
    Feb 7 at 12:04















16















Given a list of items with properties, I am trying to get the last item to appear with a maximum value of said property.



For example, for the following list of objects:



t i
A: 3
D: 7 *
F: 4
C: 5
X: 7 *
M: 6


I can get one of the Things with the highest i:



Thing t = items.stream()
.max(Comparator.comparingLong(Thing::getI))
.orElse(null);


However, this will get me Thing t = D. Is there a clean and elegant way of getting the last item, i.e. X in this case?



One possible solution is using the reduce function. However, the property is calculated on the fly and it would look more like:



Thing t = items.stream()
.reduce((left, right) ->
long leftValue = valueFunction.apply(left);
long rightValue = valueFunction.apply(right);
return leftValue > rightValue ? left : right;
)
.orElse(null);


The valueFunction now needs to be called nearly twice as often.



Other obvious roundabout solutions are:



  1. Store the object in a Tuple with its index

  2. Store the object in a Tuple with its computed value

  3. Reverse the list beforehand

  4. Don't use Streams









share|improve this question



















  • 1





    "The valueFunction now needs to be called nearly twice as often." Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D. How about you just cache the calculated value directly in the Thing instance?

    – tobias_k
    Feb 5 at 16:14












  • @tobias_k I realised that after posting my question. My practical solution was to use a standard for each.

    – Druckles
    Feb 5 at 17:35






  • 2





    If your stream goes parallel or async, there can be a complete loss of the idea of "in order". Be sure to think about this and consider if your stream can contain a field or value indicating the order, similar to a tuple of (value, orderIndex) so that the max operation is a simple, parallelizable comparison of the value and orderIndex fields.

    – ErikE
    Feb 5 at 19:19











  • @ErikE if the stream has a defined encounter order, the reduce based solution will work in parallel, as long as the reduction function fulfills the associativity constraint. Which is the case here.

    – Holger
    Feb 7 at 12:04













16












16








16


1






Given a list of items with properties, I am trying to get the last item to appear with a maximum value of said property.



For example, for the following list of objects:



t i
A: 3
D: 7 *
F: 4
C: 5
X: 7 *
M: 6


I can get one of the Things with the highest i:



Thing t = items.stream()
.max(Comparator.comparingLong(Thing::getI))
.orElse(null);


However, this will get me Thing t = D. Is there a clean and elegant way of getting the last item, i.e. X in this case?



One possible solution is using the reduce function. However, the property is calculated on the fly and it would look more like:



Thing t = items.stream()
.reduce((left, right) ->
long leftValue = valueFunction.apply(left);
long rightValue = valueFunction.apply(right);
return leftValue > rightValue ? left : right;
)
.orElse(null);


The valueFunction now needs to be called nearly twice as often.



Other obvious roundabout solutions are:



  1. Store the object in a Tuple with its index

  2. Store the object in a Tuple with its computed value

  3. Reverse the list beforehand

  4. Don't use Streams









share|improve this question
















Given a list of items with properties, I am trying to get the last item to appear with a maximum value of said property.



For example, for the following list of objects:



t i
A: 3
D: 7 *
F: 4
C: 5
X: 7 *
M: 6


I can get one of the Things with the highest i:



Thing t = items.stream()
.max(Comparator.comparingLong(Thing::getI))
.orElse(null);


However, this will get me Thing t = D. Is there a clean and elegant way of getting the last item, i.e. X in this case?



One possible solution is using the reduce function. However, the property is calculated on the fly and it would look more like:



Thing t = items.stream()
.reduce((left, right) ->
long leftValue = valueFunction.apply(left);
long rightValue = valueFunction.apply(right);
return leftValue > rightValue ? left : right;
)
.orElse(null);


The valueFunction now needs to be called nearly twice as often.



Other obvious roundabout solutions are:



  1. Store the object in a Tuple with its index

  2. Store the object in a Tuple with its computed value

  3. Reverse the list beforehand

  4. Don't use Streams






java java-8 java-stream






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Feb 5 at 14:13









nullpointer

43.2k10101200




43.2k10101200










asked Feb 5 at 13:34









DrucklesDruckles

94311033




94311033







  • 1





    "The valueFunction now needs to be called nearly twice as often." Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D. How about you just cache the calculated value directly in the Thing instance?

    – tobias_k
    Feb 5 at 16:14












  • @tobias_k I realised that after posting my question. My practical solution was to use a standard for each.

    – Druckles
    Feb 5 at 17:35






  • 2





    If your stream goes parallel or async, there can be a complete loss of the idea of "in order". Be sure to think about this and consider if your stream can contain a field or value indicating the order, similar to a tuple of (value, orderIndex) so that the max operation is a simple, parallelizable comparison of the value and orderIndex fields.

    – ErikE
    Feb 5 at 19:19











  • @ErikE if the stream has a defined encounter order, the reduce based solution will work in parallel, as long as the reduction function fulfills the associativity constraint. Which is the case here.

    – Holger
    Feb 7 at 12:04












  • 1





    "The valueFunction now needs to be called nearly twice as often." Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D. How about you just cache the calculated value directly in the Thing instance?

    – tobias_k
    Feb 5 at 16:14












  • @tobias_k I realised that after posting my question. My practical solution was to use a standard for each.

    – Druckles
    Feb 5 at 17:35






  • 2





    If your stream goes parallel or async, there can be a complete loss of the idea of "in order". Be sure to think about this and consider if your stream can contain a field or value indicating the order, similar to a tuple of (value, orderIndex) so that the max operation is a simple, parallelizable comparison of the value and orderIndex fields.

    – ErikE
    Feb 5 at 19:19











  • @ErikE if the stream has a defined encounter order, the reduce based solution will work in parallel, as long as the reduction function fulfills the associativity constraint. Which is the case here.

    – Holger
    Feb 7 at 12:04







1




1





"The valueFunction now needs to be called nearly twice as often." Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D. How about you just cache the calculated value directly in the Thing instance?

– tobias_k
Feb 5 at 16:14






"The valueFunction now needs to be called nearly twice as often." Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D. How about you just cache the calculated value directly in the Thing instance?

– tobias_k
Feb 5 at 16:14














@tobias_k I realised that after posting my question. My practical solution was to use a standard for each.

– Druckles
Feb 5 at 17:35





@tobias_k I realised that after posting my question. My practical solution was to use a standard for each.

– Druckles
Feb 5 at 17:35




2




2





If your stream goes parallel or async, there can be a complete loss of the idea of "in order". Be sure to think about this and consider if your stream can contain a field or value indicating the order, similar to a tuple of (value, orderIndex) so that the max operation is a simple, parallelizable comparison of the value and orderIndex fields.

– ErikE
Feb 5 at 19:19





If your stream goes parallel or async, there can be a complete loss of the idea of "in order". Be sure to think about this and consider if your stream can contain a field or value indicating the order, similar to a tuple of (value, orderIndex) so that the max operation is a simple, parallelizable comparison of the value and orderIndex fields.

– ErikE
Feb 5 at 19:19













@ErikE if the stream has a defined encounter order, the reduce based solution will work in parallel, as long as the reduction function fulfills the associativity constraint. Which is the case here.

– Holger
Feb 7 at 12:04





@ErikE if the stream has a defined encounter order, the reduce based solution will work in parallel, as long as the reduction function fulfills the associativity constraint. Which is the case here.

– Holger
Feb 7 at 12:04












7 Answers
7






active

oldest

votes


















6














Remove the equals option (don't return 0 if the compared numbers are equal, return -1 instead) from the comparator (ie. write your own comparator that doesn't include an equals option):



Thing t = items.stream()
.max((a, b) -> a.getI() > b.getI() ? 1 : -1)
.orElse(null);





share|improve this answer




















  • 2





    I'm not sure that java guarantees the comparator is called "in order", i.e. the later items always is the second argument (thinking about parallel execution). That being said, it makes sense to place a little bit of trust in this implementation detail.

    – WorldSEnder
    Feb 5 at 17:16






  • 1





    I've accepted this because it's the most elegant solution and, as far as I can tell, as efficient as a Comparator (which is what I wanted). If I don't want to compute the value every time I have to settle for a for each or a tuple. Thank you.

    – Druckles
    Feb 5 at 17:39






  • 2





    I do believe this is not guaranteed to work as you expect the moment that the stream goes parallel/asynch.

    – ErikE
    Feb 5 at 19:16






  • 1





    What does this have to do with equals? The only trick seems to be to invert the tie-breaking from "a unless b is greater" to "b unless a is greater", which is exactly the same as OP's reduce did, but IMHO less readable, as you return +/-1 instead of the actual object to retain. Also, this, too, will call getI more often than necessary (just as often as reduce).

    – tobias_k
    Feb 5 at 22:41











  • @tobias_k a standard comparator returns +1, 0 or -1, for a being greater than, equal, or less than b. I say 'removing the equals option' meaning my comparator only return +1 or -1, for greater than or not greater than.

    – jvdmr
    Feb 6 at 8:11


















4














Conceptually, you seem to be possibly looking for something like thenComparing using the index of the elements in the list:



Thing t = items.stream()
.max(Comparator.comparingLong(Thing::getI).thenComparing(items::indexOf))
.orElse(null);





share|improve this answer




















  • 1





    Assuming: that for two objects if attribute I is equal that doesn't mean the objects are equal as well. In which case the indexOf would return same value always.

    – nullpointer
    Feb 5 at 14:12







  • 1





    This is nice, but the problem I am trying to solve was actually originally caused by the equals method returning true for different objects ;-)

    – Druckles
    Feb 5 at 15:08







  • 2





    You're right. Unfortunately, I didn't write the equals.

    – Druckles
    Feb 5 at 15:17






  • 7





    Doesn't this give the whole thing O(n²) complexity? Also, would not work for a pure stream, with not backing list (not a requirement, but anyway). Something like Python's enumerate would come in handy here.

    – tobias_k
    Feb 5 at 15:52







  • 1





    @Marco13 in the worst case, all elements are "new maximum" values in respect to the previous elements, i.e. when the input elements happen to be in ascending order. So O(n²) is the correct time complexity of this solution.

    – Holger
    Feb 7 at 11:55


















3














To avoid the multiple applications of valueFunction in your reduce solution, simply explicitly calculate the result and put it in a tuple:



Item lastMax = items.stream()
.map(item -> new AbstractMap.SimpleEntry<Item, Long>(item, valueFunction.apply(item)))
.reduce((l, r) -> l.getValue() > r.getValue() ? l : r )
.map(Map.Entry::getKey)
.orElse(null);





share|improve this answer






























    1














    Stream is not necessary bad if you do things in two steps :



    1) Find the i value that has more occurrences in the Iterable (as you did)

    2) Search the last element for this i value by starting from the end of items:



    Thing t = 
    items.stream()
    .max(Comparator.comparingLong(Thing::getI))
    .mapping(firstMaxThing ->
    return
    IntStream.rangeClosed(1, items.size())
    .mapToObj(i -> items.get(items.size()-i))
    .filter(item -> item.getI() == firstMaxThing.getI())
    .findFirst().get();
    // here get() cannot fail as *max()* returned something.
    )
    .orElse(null)





    share|improve this answer
































      1















      The valueFunction now needs to be called nearly twice as often.




      Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D, and for longer lists, too, it seems to be called on average twice per element.



      How about you just cache the calculated value directly in the Thing instance? If this is not possible, you could use an external Map and use calculateIfAbsent to calculate the value only once for each Thing and then use your approach using reduce.



      Map<Thing, Long> cache = new HashMap<>();
      Thing x = items.stream()
      .reduce((left, right) ->
      long leftValue = cache.computeIfAbsent(left, Thing::getI);
      long rightValue = cache.computeIfAbsent(right, Thing::getI);
      return leftValue > rightValue ? left : right;
      )
      .orElse(null);


      Or a bit cleaner, calculating all the values beforehand:



      Map<Thing, Long> cache = items.stream()
      .collect(Collectors.toMap(x -> x, Thing::getI));
      Thing x = items.stream()
      .reduce((left, right) -> cache.get(left) > cache.get(right) ? left : right)
      .orElse(null);





      share|improve this answer


















      • 1





        BTW, you can easily check how often the comparator callback is called by adding some print statement or incrementing a counter in getI or in the callback itself.

        – tobias_k
        Feb 5 at 22:45


















      0














      You can still use the reduction to get this thing done. If t1 is larger, then only it will keep t1. In all the other cases it will keep t2. If either t2 is larger or t1 and t2 are the same, then it will eventually return t2 adhering to your requirement.



      Thing t = items.stream().
      reduce((t1, t2) -> t1.getI() > t2.getI() ? t1 : t2)
      .orElse(null);





      share|improve this answer

























      • This is the same solution as I presented in my question.

        – Druckles
        Feb 5 at 17:37






      • 1





        @Druckles Technically, the answer you accepted is also the same as your solution, just less obviously so.

        – tobias_k
        Feb 5 at 22:43











      • @tobias_k "Less obvious" is everything in programming.

        – Druckles
        Feb 6 at 8:53






      • 1





        @Druckles Am I getting you correctly? Your programming goal is to write as obfuscated code as possible?

        – Holger
        Feb 7 at 11:53






      • 1





        @Druckles I don't see any way, how to interpret your sentence "'Less obvious' is everything in programming" in a different way than that you like non-obvious things, like the accepted answer, which only makes it less obvious that it still does the same as the question's code, including having the same problems, plus adding another one, using a formally incorrect comparator, which makes it less obvious why it still happens to do the desired thing. "less obvious" is obfuscation. Actually, "is everything" does not only imply that you like it but that it is the most important thing to you.

        – Holger
        Feb 7 at 13:19


















      0














      Your current implementation using reduce looks good, unless your value-extractor function is costly.



      Considering later you may want to reuse the logic for different object types & fields, I would extract the logic itself in separate generic method/methods:



      public static <T, K, V> Function<T, Map.Entry<K, V>> toEntry(Function<T, K> keyFunc, Function<T, V> valueFunc)
      return t -> new AbstractMap.SimpleEntry<>(keyFunc.apply(t), valueFunc.apply(t));


      public static <ITEM, FIELD extends Comparable<FIELD>> Optional<ITEM> maxBy(Function<ITEM, FIELD> extractor, Collection<ITEM> items)
      return items.stream()
      .map(toEntry(identity(), extractor))
      .max(comparing(Map.Entry::getValue))
      .map(Map.Entry::getKey);



      The code snippets above can be used like this:



      Thing maxThing = maxBy(Thing::getField, things).orElse(null);

      AnotherThing maxAnotherThing = maxBy(AnotherThing::getAnotherField, anotherThings).orElse(null);





      share|improve this answer
























        Your Answer






        StackExchange.ifUsing("editor", function ()
        StackExchange.using("externalEditor", function ()
        StackExchange.using("snippets", function ()
        StackExchange.snippets.init();
        );
        );
        , "code-snippets");

        StackExchange.ready(function()
        var channelOptions =
        tags: "".split(" "),
        id: "1"
        ;
        initTagRenderer("".split(" "), "".split(" "), channelOptions);

        StackExchange.using("externalEditor", function()
        // Have to fire editor after snippets, if snippets enabled
        if (StackExchange.settings.snippets.snippetsEnabled)
        StackExchange.using("snippets", function()
        createEditor();
        );

        else
        createEditor();

        );

        function createEditor()
        StackExchange.prepareEditor(
        heartbeatType: 'answer',
        autoActivateHeartbeat: false,
        convertImagesToLinks: true,
        noModals: true,
        showLowRepImageUploadWarning: true,
        reputationToPostImages: 10,
        bindNavPrevention: true,
        postfix: "",
        imageUploader:
        brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
        contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
        allowUrls: true
        ,
        onDemand: true,
        discardSelector: ".discard-answer"
        ,immediatelyShowMarkdownHelp:true
        );



        );













        draft saved

        draft discarded


















        StackExchange.ready(
        function ()
        StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54535632%2fusing-java-8-stream-methods-to-get-the-last-max-value%23new-answer', 'question_page');

        );

        Post as a guest















        Required, but never shown

























        7 Answers
        7






        active

        oldest

        votes








        7 Answers
        7






        active

        oldest

        votes









        active

        oldest

        votes






        active

        oldest

        votes









        6














        Remove the equals option (don't return 0 if the compared numbers are equal, return -1 instead) from the comparator (ie. write your own comparator that doesn't include an equals option):



        Thing t = items.stream()
        .max((a, b) -> a.getI() > b.getI() ? 1 : -1)
        .orElse(null);





        share|improve this answer




















        • 2





          I'm not sure that java guarantees the comparator is called "in order", i.e. the later items always is the second argument (thinking about parallel execution). That being said, it makes sense to place a little bit of trust in this implementation detail.

          – WorldSEnder
          Feb 5 at 17:16






        • 1





          I've accepted this because it's the most elegant solution and, as far as I can tell, as efficient as a Comparator (which is what I wanted). If I don't want to compute the value every time I have to settle for a for each or a tuple. Thank you.

          – Druckles
          Feb 5 at 17:39






        • 2





          I do believe this is not guaranteed to work as you expect the moment that the stream goes parallel/asynch.

          – ErikE
          Feb 5 at 19:16






        • 1





          What does this have to do with equals? The only trick seems to be to invert the tie-breaking from "a unless b is greater" to "b unless a is greater", which is exactly the same as OP's reduce did, but IMHO less readable, as you return +/-1 instead of the actual object to retain. Also, this, too, will call getI more often than necessary (just as often as reduce).

          – tobias_k
          Feb 5 at 22:41











        • @tobias_k a standard comparator returns +1, 0 or -1, for a being greater than, equal, or less than b. I say 'removing the equals option' meaning my comparator only return +1 or -1, for greater than or not greater than.

          – jvdmr
          Feb 6 at 8:11















        6














        Remove the equals option (don't return 0 if the compared numbers are equal, return -1 instead) from the comparator (ie. write your own comparator that doesn't include an equals option):



        Thing t = items.stream()
        .max((a, b) -> a.getI() > b.getI() ? 1 : -1)
        .orElse(null);





        share|improve this answer




















        • 2





          I'm not sure that java guarantees the comparator is called "in order", i.e. the later items always is the second argument (thinking about parallel execution). That being said, it makes sense to place a little bit of trust in this implementation detail.

          – WorldSEnder
          Feb 5 at 17:16






        • 1





          I've accepted this because it's the most elegant solution and, as far as I can tell, as efficient as a Comparator (which is what I wanted). If I don't want to compute the value every time I have to settle for a for each or a tuple. Thank you.

          – Druckles
          Feb 5 at 17:39






        • 2





          I do believe this is not guaranteed to work as you expect the moment that the stream goes parallel/asynch.

          – ErikE
          Feb 5 at 19:16






        • 1





          What does this have to do with equals? The only trick seems to be to invert the tie-breaking from "a unless b is greater" to "b unless a is greater", which is exactly the same as OP's reduce did, but IMHO less readable, as you return +/-1 instead of the actual object to retain. Also, this, too, will call getI more often than necessary (just as often as reduce).

          – tobias_k
          Feb 5 at 22:41











        • @tobias_k a standard comparator returns +1, 0 or -1, for a being greater than, equal, or less than b. I say 'removing the equals option' meaning my comparator only return +1 or -1, for greater than or not greater than.

          – jvdmr
          Feb 6 at 8:11













        6












        6








        6







        Remove the equals option (don't return 0 if the compared numbers are equal, return -1 instead) from the comparator (ie. write your own comparator that doesn't include an equals option):



        Thing t = items.stream()
        .max((a, b) -> a.getI() > b.getI() ? 1 : -1)
        .orElse(null);





        share|improve this answer















        Remove the equals option (don't return 0 if the compared numbers are equal, return -1 instead) from the comparator (ie. write your own comparator that doesn't include an equals option):



        Thing t = items.stream()
        .max((a, b) -> a.getI() > b.getI() ? 1 : -1)
        .orElse(null);






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Feb 6 at 9:25

























        answered Feb 5 at 13:58









        jvdmrjvdmr

        52128




        52128







        • 2





          I'm not sure that java guarantees the comparator is called "in order", i.e. the later items always is the second argument (thinking about parallel execution). That being said, it makes sense to place a little bit of trust in this implementation detail.

          – WorldSEnder
          Feb 5 at 17:16






        • 1





          I've accepted this because it's the most elegant solution and, as far as I can tell, as efficient as a Comparator (which is what I wanted). If I don't want to compute the value every time I have to settle for a for each or a tuple. Thank you.

          – Druckles
          Feb 5 at 17:39






        • 2





          I do believe this is not guaranteed to work as you expect the moment that the stream goes parallel/asynch.

          – ErikE
          Feb 5 at 19:16






        • 1





          What does this have to do with equals? The only trick seems to be to invert the tie-breaking from "a unless b is greater" to "b unless a is greater", which is exactly the same as OP's reduce did, but IMHO less readable, as you return +/-1 instead of the actual object to retain. Also, this, too, will call getI more often than necessary (just as often as reduce).

          – tobias_k
          Feb 5 at 22:41











        • @tobias_k a standard comparator returns +1, 0 or -1, for a being greater than, equal, or less than b. I say 'removing the equals option' meaning my comparator only return +1 or -1, for greater than or not greater than.

          – jvdmr
          Feb 6 at 8:11












        • 2





          I'm not sure that java guarantees the comparator is called "in order", i.e. the later items always is the second argument (thinking about parallel execution). That being said, it makes sense to place a little bit of trust in this implementation detail.

          – WorldSEnder
          Feb 5 at 17:16






        • 1





          I've accepted this because it's the most elegant solution and, as far as I can tell, as efficient as a Comparator (which is what I wanted). If I don't want to compute the value every time I have to settle for a for each or a tuple. Thank you.

          – Druckles
          Feb 5 at 17:39






        • 2





          I do believe this is not guaranteed to work as you expect the moment that the stream goes parallel/asynch.

          – ErikE
          Feb 5 at 19:16






        • 1





          What does this have to do with equals? The only trick seems to be to invert the tie-breaking from "a unless b is greater" to "b unless a is greater", which is exactly the same as OP's reduce did, but IMHO less readable, as you return +/-1 instead of the actual object to retain. Also, this, too, will call getI more often than necessary (just as often as reduce).

          – tobias_k
          Feb 5 at 22:41











        • @tobias_k a standard comparator returns +1, 0 or -1, for a being greater than, equal, or less than b. I say 'removing the equals option' meaning my comparator only return +1 or -1, for greater than or not greater than.

          – jvdmr
          Feb 6 at 8:11







        2




        2





        I'm not sure that java guarantees the comparator is called "in order", i.e. the later items always is the second argument (thinking about parallel execution). That being said, it makes sense to place a little bit of trust in this implementation detail.

        – WorldSEnder
        Feb 5 at 17:16





        I'm not sure that java guarantees the comparator is called "in order", i.e. the later items always is the second argument (thinking about parallel execution). That being said, it makes sense to place a little bit of trust in this implementation detail.

        – WorldSEnder
        Feb 5 at 17:16




        1




        1





        I've accepted this because it's the most elegant solution and, as far as I can tell, as efficient as a Comparator (which is what I wanted). If I don't want to compute the value every time I have to settle for a for each or a tuple. Thank you.

        – Druckles
        Feb 5 at 17:39





        I've accepted this because it's the most elegant solution and, as far as I can tell, as efficient as a Comparator (which is what I wanted). If I don't want to compute the value every time I have to settle for a for each or a tuple. Thank you.

        – Druckles
        Feb 5 at 17:39




        2




        2





        I do believe this is not guaranteed to work as you expect the moment that the stream goes parallel/asynch.

        – ErikE
        Feb 5 at 19:16





        I do believe this is not guaranteed to work as you expect the moment that the stream goes parallel/asynch.

        – ErikE
        Feb 5 at 19:16




        1




        1





        What does this have to do with equals? The only trick seems to be to invert the tie-breaking from "a unless b is greater" to "b unless a is greater", which is exactly the same as OP's reduce did, but IMHO less readable, as you return +/-1 instead of the actual object to retain. Also, this, too, will call getI more often than necessary (just as often as reduce).

        – tobias_k
        Feb 5 at 22:41





        What does this have to do with equals? The only trick seems to be to invert the tie-breaking from "a unless b is greater" to "b unless a is greater", which is exactly the same as OP's reduce did, but IMHO less readable, as you return +/-1 instead of the actual object to retain. Also, this, too, will call getI more often than necessary (just as often as reduce).

        – tobias_k
        Feb 5 at 22:41













        @tobias_k a standard comparator returns +1, 0 or -1, for a being greater than, equal, or less than b. I say 'removing the equals option' meaning my comparator only return +1 or -1, for greater than or not greater than.

        – jvdmr
        Feb 6 at 8:11





        @tobias_k a standard comparator returns +1, 0 or -1, for a being greater than, equal, or less than b. I say 'removing the equals option' meaning my comparator only return +1 or -1, for greater than or not greater than.

        – jvdmr
        Feb 6 at 8:11













        4














        Conceptually, you seem to be possibly looking for something like thenComparing using the index of the elements in the list:



        Thing t = items.stream()
        .max(Comparator.comparingLong(Thing::getI).thenComparing(items::indexOf))
        .orElse(null);





        share|improve this answer




















        • 1





          Assuming: that for two objects if attribute I is equal that doesn't mean the objects are equal as well. In which case the indexOf would return same value always.

          – nullpointer
          Feb 5 at 14:12







        • 1





          This is nice, but the problem I am trying to solve was actually originally caused by the equals method returning true for different objects ;-)

          – Druckles
          Feb 5 at 15:08







        • 2





          You're right. Unfortunately, I didn't write the equals.

          – Druckles
          Feb 5 at 15:17






        • 7





          Doesn't this give the whole thing O(n²) complexity? Also, would not work for a pure stream, with not backing list (not a requirement, but anyway). Something like Python's enumerate would come in handy here.

          – tobias_k
          Feb 5 at 15:52







        • 1





          @Marco13 in the worst case, all elements are "new maximum" values in respect to the previous elements, i.e. when the input elements happen to be in ascending order. So O(n²) is the correct time complexity of this solution.

          – Holger
          Feb 7 at 11:55















        4














        Conceptually, you seem to be possibly looking for something like thenComparing using the index of the elements in the list:



        Thing t = items.stream()
        .max(Comparator.comparingLong(Thing::getI).thenComparing(items::indexOf))
        .orElse(null);





        share|improve this answer




















        • 1





          Assuming: that for two objects if attribute I is equal that doesn't mean the objects are equal as well. In which case the indexOf would return same value always.

          – nullpointer
          Feb 5 at 14:12







        • 1





          This is nice, but the problem I am trying to solve was actually originally caused by the equals method returning true for different objects ;-)

          – Druckles
          Feb 5 at 15:08







        • 2





          You're right. Unfortunately, I didn't write the equals.

          – Druckles
          Feb 5 at 15:17






        • 7





          Doesn't this give the whole thing O(n²) complexity? Also, would not work for a pure stream, with not backing list (not a requirement, but anyway). Something like Python's enumerate would come in handy here.

          – tobias_k
          Feb 5 at 15:52







        • 1





          @Marco13 in the worst case, all elements are "new maximum" values in respect to the previous elements, i.e. when the input elements happen to be in ascending order. So O(n²) is the correct time complexity of this solution.

          – Holger
          Feb 7 at 11:55













        4












        4








        4







        Conceptually, you seem to be possibly looking for something like thenComparing using the index of the elements in the list:



        Thing t = items.stream()
        .max(Comparator.comparingLong(Thing::getI).thenComparing(items::indexOf))
        .orElse(null);





        share|improve this answer















        Conceptually, you seem to be possibly looking for something like thenComparing using the index of the elements in the list:



        Thing t = items.stream()
        .max(Comparator.comparingLong(Thing::getI).thenComparing(items::indexOf))
        .orElse(null);






        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Feb 5 at 14:32

























        answered Feb 5 at 14:09









        nullpointernullpointer

        43.2k10101200




        43.2k10101200







        • 1





          Assuming: that for two objects if attribute I is equal that doesn't mean the objects are equal as well. In which case the indexOf would return same value always.

          – nullpointer
          Feb 5 at 14:12







        • 1





          This is nice, but the problem I am trying to solve was actually originally caused by the equals method returning true for different objects ;-)

          – Druckles
          Feb 5 at 15:08







        • 2





          You're right. Unfortunately, I didn't write the equals.

          – Druckles
          Feb 5 at 15:17






        • 7





          Doesn't this give the whole thing O(n²) complexity? Also, would not work for a pure stream, with not backing list (not a requirement, but anyway). Something like Python's enumerate would come in handy here.

          – tobias_k
          Feb 5 at 15:52







        • 1





          @Marco13 in the worst case, all elements are "new maximum" values in respect to the previous elements, i.e. when the input elements happen to be in ascending order. So O(n²) is the correct time complexity of this solution.

          – Holger
          Feb 7 at 11:55












        • 1





          Assuming: that for two objects if attribute I is equal that doesn't mean the objects are equal as well. In which case the indexOf would return same value always.

          – nullpointer
          Feb 5 at 14:12







        • 1





          This is nice, but the problem I am trying to solve was actually originally caused by the equals method returning true for different objects ;-)

          – Druckles
          Feb 5 at 15:08







        • 2





          You're right. Unfortunately, I didn't write the equals.

          – Druckles
          Feb 5 at 15:17






        • 7





          Doesn't this give the whole thing O(n²) complexity? Also, would not work for a pure stream, with not backing list (not a requirement, but anyway). Something like Python's enumerate would come in handy here.

          – tobias_k
          Feb 5 at 15:52







        • 1





          @Marco13 in the worst case, all elements are "new maximum" values in respect to the previous elements, i.e. when the input elements happen to be in ascending order. So O(n²) is the correct time complexity of this solution.

          – Holger
          Feb 7 at 11:55







        1




        1





        Assuming: that for two objects if attribute I is equal that doesn't mean the objects are equal as well. In which case the indexOf would return same value always.

        – nullpointer
        Feb 5 at 14:12






        Assuming: that for two objects if attribute I is equal that doesn't mean the objects are equal as well. In which case the indexOf would return same value always.

        – nullpointer
        Feb 5 at 14:12





        1




        1





        This is nice, but the problem I am trying to solve was actually originally caused by the equals method returning true for different objects ;-)

        – Druckles
        Feb 5 at 15:08






        This is nice, but the problem I am trying to solve was actually originally caused by the equals method returning true for different objects ;-)

        – Druckles
        Feb 5 at 15:08





        2




        2





        You're right. Unfortunately, I didn't write the equals.

        – Druckles
        Feb 5 at 15:17





        You're right. Unfortunately, I didn't write the equals.

        – Druckles
        Feb 5 at 15:17




        7




        7





        Doesn't this give the whole thing O(n²) complexity? Also, would not work for a pure stream, with not backing list (not a requirement, but anyway). Something like Python's enumerate would come in handy here.

        – tobias_k
        Feb 5 at 15:52






        Doesn't this give the whole thing O(n²) complexity? Also, would not work for a pure stream, with not backing list (not a requirement, but anyway). Something like Python's enumerate would come in handy here.

        – tobias_k
        Feb 5 at 15:52





        1




        1





        @Marco13 in the worst case, all elements are "new maximum" values in respect to the previous elements, i.e. when the input elements happen to be in ascending order. So O(n²) is the correct time complexity of this solution.

        – Holger
        Feb 7 at 11:55





        @Marco13 in the worst case, all elements are "new maximum" values in respect to the previous elements, i.e. when the input elements happen to be in ascending order. So O(n²) is the correct time complexity of this solution.

        – Holger
        Feb 7 at 11:55











        3














        To avoid the multiple applications of valueFunction in your reduce solution, simply explicitly calculate the result and put it in a tuple:



        Item lastMax = items.stream()
        .map(item -> new AbstractMap.SimpleEntry<Item, Long>(item, valueFunction.apply(item)))
        .reduce((l, r) -> l.getValue() > r.getValue() ? l : r )
        .map(Map.Entry::getKey)
        .orElse(null);





        share|improve this answer



























          3














          To avoid the multiple applications of valueFunction in your reduce solution, simply explicitly calculate the result and put it in a tuple:



          Item lastMax = items.stream()
          .map(item -> new AbstractMap.SimpleEntry<Item, Long>(item, valueFunction.apply(item)))
          .reduce((l, r) -> l.getValue() > r.getValue() ? l : r )
          .map(Map.Entry::getKey)
          .orElse(null);





          share|improve this answer

























            3












            3








            3







            To avoid the multiple applications of valueFunction in your reduce solution, simply explicitly calculate the result and put it in a tuple:



            Item lastMax = items.stream()
            .map(item -> new AbstractMap.SimpleEntry<Item, Long>(item, valueFunction.apply(item)))
            .reduce((l, r) -> l.getValue() > r.getValue() ? l : r )
            .map(Map.Entry::getKey)
            .orElse(null);





            share|improve this answer













            To avoid the multiple applications of valueFunction in your reduce solution, simply explicitly calculate the result and put it in a tuple:



            Item lastMax = items.stream()
            .map(item -> new AbstractMap.SimpleEntry<Item, Long>(item, valueFunction.apply(item)))
            .reduce((l, r) -> l.getValue() > r.getValue() ? l : r )
            .map(Map.Entry::getKey)
            .orElse(null);






            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Feb 5 at 14:53









            MikeFHayMikeFHay

            3,14732035




            3,14732035





















                1














                Stream is not necessary bad if you do things in two steps :



                1) Find the i value that has more occurrences in the Iterable (as you did)

                2) Search the last element for this i value by starting from the end of items:



                Thing t = 
                items.stream()
                .max(Comparator.comparingLong(Thing::getI))
                .mapping(firstMaxThing ->
                return
                IntStream.rangeClosed(1, items.size())
                .mapToObj(i -> items.get(items.size()-i))
                .filter(item -> item.getI() == firstMaxThing.getI())
                .findFirst().get();
                // here get() cannot fail as *max()* returned something.
                )
                .orElse(null)





                share|improve this answer





























                  1














                  Stream is not necessary bad if you do things in two steps :



                  1) Find the i value that has more occurrences in the Iterable (as you did)

                  2) Search the last element for this i value by starting from the end of items:



                  Thing t = 
                  items.stream()
                  .max(Comparator.comparingLong(Thing::getI))
                  .mapping(firstMaxThing ->
                  return
                  IntStream.rangeClosed(1, items.size())
                  .mapToObj(i -> items.get(items.size()-i))
                  .filter(item -> item.getI() == firstMaxThing.getI())
                  .findFirst().get();
                  // here get() cannot fail as *max()* returned something.
                  )
                  .orElse(null)





                  share|improve this answer



























                    1












                    1








                    1







                    Stream is not necessary bad if you do things in two steps :



                    1) Find the i value that has more occurrences in the Iterable (as you did)

                    2) Search the last element for this i value by starting from the end of items:



                    Thing t = 
                    items.stream()
                    .max(Comparator.comparingLong(Thing::getI))
                    .mapping(firstMaxThing ->
                    return
                    IntStream.rangeClosed(1, items.size())
                    .mapToObj(i -> items.get(items.size()-i))
                    .filter(item -> item.getI() == firstMaxThing.getI())
                    .findFirst().get();
                    // here get() cannot fail as *max()* returned something.
                    )
                    .orElse(null)





                    share|improve this answer















                    Stream is not necessary bad if you do things in two steps :



                    1) Find the i value that has more occurrences in the Iterable (as you did)

                    2) Search the last element for this i value by starting from the end of items:



                    Thing t = 
                    items.stream()
                    .max(Comparator.comparingLong(Thing::getI))
                    .mapping(firstMaxThing ->
                    return
                    IntStream.rangeClosed(1, items.size())
                    .mapToObj(i -> items.get(items.size()-i))
                    .filter(item -> item.getI() == firstMaxThing.getI())
                    .findFirst().get();
                    // here get() cannot fail as *max()* returned something.
                    )
                    .orElse(null)






                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Feb 5 at 15:14

























                    answered Feb 5 at 15:00









                    davidxxxdavidxxx

                    67.1k67095




                    67.1k67095





















                        1















                        The valueFunction now needs to be called nearly twice as often.




                        Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D, and for longer lists, too, it seems to be called on average twice per element.



                        How about you just cache the calculated value directly in the Thing instance? If this is not possible, you could use an external Map and use calculateIfAbsent to calculate the value only once for each Thing and then use your approach using reduce.



                        Map<Thing, Long> cache = new HashMap<>();
                        Thing x = items.stream()
                        .reduce((left, right) ->
                        long leftValue = cache.computeIfAbsent(left, Thing::getI);
                        long rightValue = cache.computeIfAbsent(right, Thing::getI);
                        return leftValue > rightValue ? left : right;
                        )
                        .orElse(null);


                        Or a bit cleaner, calculating all the values beforehand:



                        Map<Thing, Long> cache = items.stream()
                        .collect(Collectors.toMap(x -> x, Thing::getI));
                        Thing x = items.stream()
                        .reduce((left, right) -> cache.get(left) > cache.get(right) ? left : right)
                        .orElse(null);





                        share|improve this answer


















                        • 1





                          BTW, you can easily check how often the comparator callback is called by adding some print statement or incrementing a counter in getI or in the callback itself.

                          – tobias_k
                          Feb 5 at 22:45















                        1















                        The valueFunction now needs to be called nearly twice as often.




                        Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D, and for longer lists, too, it seems to be called on average twice per element.



                        How about you just cache the calculated value directly in the Thing instance? If this is not possible, you could use an external Map and use calculateIfAbsent to calculate the value only once for each Thing and then use your approach using reduce.



                        Map<Thing, Long> cache = new HashMap<>();
                        Thing x = items.stream()
                        .reduce((left, right) ->
                        long leftValue = cache.computeIfAbsent(left, Thing::getI);
                        long rightValue = cache.computeIfAbsent(right, Thing::getI);
                        return leftValue > rightValue ? left : right;
                        )
                        .orElse(null);


                        Or a bit cleaner, calculating all the values beforehand:



                        Map<Thing, Long> cache = items.stream()
                        .collect(Collectors.toMap(x -> x, Thing::getI));
                        Thing x = items.stream()
                        .reduce((left, right) -> cache.get(left) > cache.get(right) ? left : right)
                        .orElse(null);





                        share|improve this answer


















                        • 1





                          BTW, you can easily check how often the comparator callback is called by adding some print statement or incrementing a counter in getI or in the callback itself.

                          – tobias_k
                          Feb 5 at 22:45













                        1












                        1








                        1








                        The valueFunction now needs to be called nearly twice as often.




                        Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D, and for longer lists, too, it seems to be called on average twice per element.



                        How about you just cache the calculated value directly in the Thing instance? If this is not possible, you could use an external Map and use calculateIfAbsent to calculate the value only once for each Thing and then use your approach using reduce.



                        Map<Thing, Long> cache = new HashMap<>();
                        Thing x = items.stream()
                        .reduce((left, right) ->
                        long leftValue = cache.computeIfAbsent(left, Thing::getI);
                        long rightValue = cache.computeIfAbsent(right, Thing::getI);
                        return leftValue > rightValue ? left : right;
                        )
                        .orElse(null);


                        Or a bit cleaner, calculating all the values beforehand:



                        Map<Thing, Long> cache = items.stream()
                        .collect(Collectors.toMap(x -> x, Thing::getI));
                        Thing x = items.stream()
                        .reduce((left, right) -> cache.get(left) > cache.get(right) ? left : right)
                        .orElse(null);





                        share|improve this answer














                        The valueFunction now needs to be called nearly twice as often.




                        Note that even when using max, the getI method will be called again and again for every comparison, not just once per element. In your example, it's called 11 times, including 6 times for D, and for longer lists, too, it seems to be called on average twice per element.



                        How about you just cache the calculated value directly in the Thing instance? If this is not possible, you could use an external Map and use calculateIfAbsent to calculate the value only once for each Thing and then use your approach using reduce.



                        Map<Thing, Long> cache = new HashMap<>();
                        Thing x = items.stream()
                        .reduce((left, right) ->
                        long leftValue = cache.computeIfAbsent(left, Thing::getI);
                        long rightValue = cache.computeIfAbsent(right, Thing::getI);
                        return leftValue > rightValue ? left : right;
                        )
                        .orElse(null);


                        Or a bit cleaner, calculating all the values beforehand:



                        Map<Thing, Long> cache = items.stream()
                        .collect(Collectors.toMap(x -> x, Thing::getI));
                        Thing x = items.stream()
                        .reduce((left, right) -> cache.get(left) > cache.get(right) ? left : right)
                        .orElse(null);






                        share|improve this answer












                        share|improve this answer



                        share|improve this answer










                        answered Feb 5 at 16:40









                        tobias_ktobias_k

                        58.7k969107




                        58.7k969107







                        • 1





                          BTW, you can easily check how often the comparator callback is called by adding some print statement or incrementing a counter in getI or in the callback itself.

                          – tobias_k
                          Feb 5 at 22:45












                        • 1





                          BTW, you can easily check how often the comparator callback is called by adding some print statement or incrementing a counter in getI or in the callback itself.

                          – tobias_k
                          Feb 5 at 22:45







                        1




                        1





                        BTW, you can easily check how often the comparator callback is called by adding some print statement or incrementing a counter in getI or in the callback itself.

                        – tobias_k
                        Feb 5 at 22:45





                        BTW, you can easily check how often the comparator callback is called by adding some print statement or incrementing a counter in getI or in the callback itself.

                        – tobias_k
                        Feb 5 at 22:45











                        0














                        You can still use the reduction to get this thing done. If t1 is larger, then only it will keep t1. In all the other cases it will keep t2. If either t2 is larger or t1 and t2 are the same, then it will eventually return t2 adhering to your requirement.



                        Thing t = items.stream().
                        reduce((t1, t2) -> t1.getI() > t2.getI() ? t1 : t2)
                        .orElse(null);





                        share|improve this answer

























                        • This is the same solution as I presented in my question.

                          – Druckles
                          Feb 5 at 17:37






                        • 1





                          @Druckles Technically, the answer you accepted is also the same as your solution, just less obviously so.

                          – tobias_k
                          Feb 5 at 22:43











                        • @tobias_k "Less obvious" is everything in programming.

                          – Druckles
                          Feb 6 at 8:53






                        • 1





                          @Druckles Am I getting you correctly? Your programming goal is to write as obfuscated code as possible?

                          – Holger
                          Feb 7 at 11:53






                        • 1





                          @Druckles I don't see any way, how to interpret your sentence "'Less obvious' is everything in programming" in a different way than that you like non-obvious things, like the accepted answer, which only makes it less obvious that it still does the same as the question's code, including having the same problems, plus adding another one, using a formally incorrect comparator, which makes it less obvious why it still happens to do the desired thing. "less obvious" is obfuscation. Actually, "is everything" does not only imply that you like it but that it is the most important thing to you.

                          – Holger
                          Feb 7 at 13:19















                        0














                        You can still use the reduction to get this thing done. If t1 is larger, then only it will keep t1. In all the other cases it will keep t2. If either t2 is larger or t1 and t2 are the same, then it will eventually return t2 adhering to your requirement.



                        Thing t = items.stream().
                        reduce((t1, t2) -> t1.getI() > t2.getI() ? t1 : t2)
                        .orElse(null);





                        share|improve this answer

























                        • This is the same solution as I presented in my question.

                          – Druckles
                          Feb 5 at 17:37






                        • 1





                          @Druckles Technically, the answer you accepted is also the same as your solution, just less obviously so.

                          – tobias_k
                          Feb 5 at 22:43











                        • @tobias_k "Less obvious" is everything in programming.

                          – Druckles
                          Feb 6 at 8:53






                        • 1





                          @Druckles Am I getting you correctly? Your programming goal is to write as obfuscated code as possible?

                          – Holger
                          Feb 7 at 11:53






                        • 1





                          @Druckles I don't see any way, how to interpret your sentence "'Less obvious' is everything in programming" in a different way than that you like non-obvious things, like the accepted answer, which only makes it less obvious that it still does the same as the question's code, including having the same problems, plus adding another one, using a formally incorrect comparator, which makes it less obvious why it still happens to do the desired thing. "less obvious" is obfuscation. Actually, "is everything" does not only imply that you like it but that it is the most important thing to you.

                          – Holger
                          Feb 7 at 13:19













                        0












                        0








                        0







                        You can still use the reduction to get this thing done. If t1 is larger, then only it will keep t1. In all the other cases it will keep t2. If either t2 is larger or t1 and t2 are the same, then it will eventually return t2 adhering to your requirement.



                        Thing t = items.stream().
                        reduce((t1, t2) -> t1.getI() > t2.getI() ? t1 : t2)
                        .orElse(null);





                        share|improve this answer















                        You can still use the reduction to get this thing done. If t1 is larger, then only it will keep t1. In all the other cases it will keep t2. If either t2 is larger or t1 and t2 are the same, then it will eventually return t2 adhering to your requirement.



                        Thing t = items.stream().
                        reduce((t1, t2) -> t1.getI() > t2.getI() ? t1 : t2)
                        .orElse(null);






                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Feb 5 at 14:30

























                        answered Feb 5 at 13:39









                        Ravindra RanwalaRavindra Ranwala

                        10.6k42040




                        10.6k42040












                        • This is the same solution as I presented in my question.

                          – Druckles
                          Feb 5 at 17:37






                        • 1





                          @Druckles Technically, the answer you accepted is also the same as your solution, just less obviously so.

                          – tobias_k
                          Feb 5 at 22:43











                        • @tobias_k "Less obvious" is everything in programming.

                          – Druckles
                          Feb 6 at 8:53






                        • 1





                          @Druckles Am I getting you correctly? Your programming goal is to write as obfuscated code as possible?

                          – Holger
                          Feb 7 at 11:53






                        • 1





                          @Druckles I don't see any way, how to interpret your sentence "'Less obvious' is everything in programming" in a different way than that you like non-obvious things, like the accepted answer, which only makes it less obvious that it still does the same as the question's code, including having the same problems, plus adding another one, using a formally incorrect comparator, which makes it less obvious why it still happens to do the desired thing. "less obvious" is obfuscation. Actually, "is everything" does not only imply that you like it but that it is the most important thing to you.

                          – Holger
                          Feb 7 at 13:19

















                        • This is the same solution as I presented in my question.

                          – Druckles
                          Feb 5 at 17:37






                        • 1





                          @Druckles Technically, the answer you accepted is also the same as your solution, just less obviously so.

                          – tobias_k
                          Feb 5 at 22:43











                        • @tobias_k "Less obvious" is everything in programming.

                          – Druckles
                          Feb 6 at 8:53






                        • 1





                          @Druckles Am I getting you correctly? Your programming goal is to write as obfuscated code as possible?

                          – Holger
                          Feb 7 at 11:53






                        • 1





                          @Druckles I don't see any way, how to interpret your sentence "'Less obvious' is everything in programming" in a different way than that you like non-obvious things, like the accepted answer, which only makes it less obvious that it still does the same as the question's code, including having the same problems, plus adding another one, using a formally incorrect comparator, which makes it less obvious why it still happens to do the desired thing. "less obvious" is obfuscation. Actually, "is everything" does not only imply that you like it but that it is the most important thing to you.

                          – Holger
                          Feb 7 at 13:19
















                        This is the same solution as I presented in my question.

                        – Druckles
                        Feb 5 at 17:37





                        This is the same solution as I presented in my question.

                        – Druckles
                        Feb 5 at 17:37




                        1




                        1





                        @Druckles Technically, the answer you accepted is also the same as your solution, just less obviously so.

                        – tobias_k
                        Feb 5 at 22:43





                        @Druckles Technically, the answer you accepted is also the same as your solution, just less obviously so.

                        – tobias_k
                        Feb 5 at 22:43













                        @tobias_k "Less obvious" is everything in programming.

                        – Druckles
                        Feb 6 at 8:53





                        @tobias_k "Less obvious" is everything in programming.

                        – Druckles
                        Feb 6 at 8:53




                        1




                        1





                        @Druckles Am I getting you correctly? Your programming goal is to write as obfuscated code as possible?

                        – Holger
                        Feb 7 at 11:53





                        @Druckles Am I getting you correctly? Your programming goal is to write as obfuscated code as possible?

                        – Holger
                        Feb 7 at 11:53




                        1




                        1





                        @Druckles I don't see any way, how to interpret your sentence "'Less obvious' is everything in programming" in a different way than that you like non-obvious things, like the accepted answer, which only makes it less obvious that it still does the same as the question's code, including having the same problems, plus adding another one, using a formally incorrect comparator, which makes it less obvious why it still happens to do the desired thing. "less obvious" is obfuscation. Actually, "is everything" does not only imply that you like it but that it is the most important thing to you.

                        – Holger
                        Feb 7 at 13:19





                        @Druckles I don't see any way, how to interpret your sentence "'Less obvious' is everything in programming" in a different way than that you like non-obvious things, like the accepted answer, which only makes it less obvious that it still does the same as the question's code, including having the same problems, plus adding another one, using a formally incorrect comparator, which makes it less obvious why it still happens to do the desired thing. "less obvious" is obfuscation. Actually, "is everything" does not only imply that you like it but that it is the most important thing to you.

                        – Holger
                        Feb 7 at 13:19











                        0














                        Your current implementation using reduce looks good, unless your value-extractor function is costly.



                        Considering later you may want to reuse the logic for different object types & fields, I would extract the logic itself in separate generic method/methods:



                        public static <T, K, V> Function<T, Map.Entry<K, V>> toEntry(Function<T, K> keyFunc, Function<T, V> valueFunc)
                        return t -> new AbstractMap.SimpleEntry<>(keyFunc.apply(t), valueFunc.apply(t));


                        public static <ITEM, FIELD extends Comparable<FIELD>> Optional<ITEM> maxBy(Function<ITEM, FIELD> extractor, Collection<ITEM> items)
                        return items.stream()
                        .map(toEntry(identity(), extractor))
                        .max(comparing(Map.Entry::getValue))
                        .map(Map.Entry::getKey);



                        The code snippets above can be used like this:



                        Thing maxThing = maxBy(Thing::getField, things).orElse(null);

                        AnotherThing maxAnotherThing = maxBy(AnotherThing::getAnotherField, anotherThings).orElse(null);





                        share|improve this answer





























                          0














                          Your current implementation using reduce looks good, unless your value-extractor function is costly.



                          Considering later you may want to reuse the logic for different object types & fields, I would extract the logic itself in separate generic method/methods:



                          public static <T, K, V> Function<T, Map.Entry<K, V>> toEntry(Function<T, K> keyFunc, Function<T, V> valueFunc)
                          return t -> new AbstractMap.SimpleEntry<>(keyFunc.apply(t), valueFunc.apply(t));


                          public static <ITEM, FIELD extends Comparable<FIELD>> Optional<ITEM> maxBy(Function<ITEM, FIELD> extractor, Collection<ITEM> items)
                          return items.stream()
                          .map(toEntry(identity(), extractor))
                          .max(comparing(Map.Entry::getValue))
                          .map(Map.Entry::getKey);



                          The code snippets above can be used like this:



                          Thing maxThing = maxBy(Thing::getField, things).orElse(null);

                          AnotherThing maxAnotherThing = maxBy(AnotherThing::getAnotherField, anotherThings).orElse(null);





                          share|improve this answer



























                            0












                            0








                            0







                            Your current implementation using reduce looks good, unless your value-extractor function is costly.



                            Considering later you may want to reuse the logic for different object types & fields, I would extract the logic itself in separate generic method/methods:



                            public static <T, K, V> Function<T, Map.Entry<K, V>> toEntry(Function<T, K> keyFunc, Function<T, V> valueFunc)
                            return t -> new AbstractMap.SimpleEntry<>(keyFunc.apply(t), valueFunc.apply(t));


                            public static <ITEM, FIELD extends Comparable<FIELD>> Optional<ITEM> maxBy(Function<ITEM, FIELD> extractor, Collection<ITEM> items)
                            return items.stream()
                            .map(toEntry(identity(), extractor))
                            .max(comparing(Map.Entry::getValue))
                            .map(Map.Entry::getKey);



                            The code snippets above can be used like this:



                            Thing maxThing = maxBy(Thing::getField, things).orElse(null);

                            AnotherThing maxAnotherThing = maxBy(AnotherThing::getAnotherField, anotherThings).orElse(null);





                            share|improve this answer















                            Your current implementation using reduce looks good, unless your value-extractor function is costly.



                            Considering later you may want to reuse the logic for different object types & fields, I would extract the logic itself in separate generic method/methods:



                            public static <T, K, V> Function<T, Map.Entry<K, V>> toEntry(Function<T, K> keyFunc, Function<T, V> valueFunc)
                            return t -> new AbstractMap.SimpleEntry<>(keyFunc.apply(t), valueFunc.apply(t));


                            public static <ITEM, FIELD extends Comparable<FIELD>> Optional<ITEM> maxBy(Function<ITEM, FIELD> extractor, Collection<ITEM> items)
                            return items.stream()
                            .map(toEntry(identity(), extractor))
                            .max(comparing(Map.Entry::getValue))
                            .map(Map.Entry::getKey);



                            The code snippets above can be used like this:



                            Thing maxThing = maxBy(Thing::getField, things).orElse(null);

                            AnotherThing maxAnotherThing = maxBy(AnotherThing::getAnotherField, anotherThings).orElse(null);






                            share|improve this answer














                            share|improve this answer



                            share|improve this answer








                            edited Feb 5 at 17:01

























                            answered Feb 5 at 14:30









                            ETOETO

                            2,6281628




                            2,6281628



























                                draft saved

                                draft discarded
















































                                Thanks for contributing an answer to Stack Overflow!


                                • Please be sure to answer the question. Provide details and share your research!

                                But avoid


                                • Asking for help, clarification, or responding to other answers.

                                • Making statements based on opinion; back them up with references or personal experience.

                                To learn more, see our tips on writing great answers.




                                draft saved


                                draft discarded














                                StackExchange.ready(
                                function ()
                                StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54535632%2fusing-java-8-stream-methods-to-get-the-last-max-value%23new-answer', 'question_page');

                                );

                                Post as a guest















                                Required, but never shown





















































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown

































                                Required, but never shown














                                Required, but never shown












                                Required, but never shown







                                Required, but never shown






                                Popular posts from this blog

                                How to check contact read email or not when send email to Individual?

                                Displaying single band from multi-band raster using QGIS

                                How many registers does an x86_64 CPU actually have?