Using Java 8 stream methods to get the last max value












11















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
    48 mins ago


















11















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
    48 mins ago
















11












11








11


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 2 hours ago









nullpointer

47.6k11100192




47.6k11100192










asked 3 hours ago









DrucklesDruckles

89211033




89211033








  • 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
    48 mins ago
















  • 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
    48 mins ago










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
48 mins ago







"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
48 mins ago














7 Answers
7






active

oldest

votes


















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
    2 hours ago








  • 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
    1 hour ago













  • @Druckles I think then you're trying to solve it in an incorrect way probably.

    – nullpointer
    1 hour ago






  • 2





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

    – Druckles
    1 hour ago






  • 1





    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
    1 hour ago





















2














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














    Remove the equals option 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































      1














      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


























      • There is no t or getT() in this case, it's simply used as a label in this example. And it can't be used to order the Stream; it's the order of the items in the original list that is important.

        – Druckles
        3 hours ago



















      0














      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

































        0















        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































          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









            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
              2 hours ago








            • 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
              1 hour ago













            • @Druckles I think then you're trying to solve it in an incorrect way probably.

              – nullpointer
              1 hour ago






            • 2





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

              – Druckles
              1 hour ago






            • 1





              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
              1 hour ago


















            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
              2 hours ago








            • 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
              1 hour ago













            • @Druckles I think then you're trying to solve it in an incorrect way probably.

              – nullpointer
              1 hour ago






            • 2





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

              – Druckles
              1 hour ago






            • 1





              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
              1 hour ago
















            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 2 hours ago

























            answered 2 hours ago









            nullpointernullpointer

            47.6k11100192




            47.6k11100192








            • 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
              2 hours ago








            • 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
              1 hour ago













            • @Druckles I think then you're trying to solve it in an incorrect way probably.

              – nullpointer
              1 hour ago






            • 2





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

              – Druckles
              1 hour ago






            • 1





              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
              1 hour ago
















            • 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
              2 hours ago








            • 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
              1 hour ago













            • @Druckles I think then you're trying to solve it in an incorrect way probably.

              – nullpointer
              1 hour ago






            • 2





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

              – Druckles
              1 hour ago






            • 1





              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
              1 hour ago










            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
            2 hours ago







            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
            2 hours ago






            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
            1 hour ago







            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
            1 hour ago















            @Druckles I think then you're trying to solve it in an incorrect way probably.

            – nullpointer
            1 hour ago





            @Druckles I think then you're trying to solve it in an incorrect way probably.

            – nullpointer
            1 hour ago




            2




            2





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

            – Druckles
            1 hour ago





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

            – Druckles
            1 hour ago




            1




            1





            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
            1 hour ago







            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
            1 hour ago















            2














            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




























              2














              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


























                2












                2








                2







                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 2 hours ago









                MikeFHayMikeFHay

                3,13032035




                3,13032035























                    1














                    Remove the equals option 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




























                      1














                      Remove the equals option 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


























                        1












                        1








                        1







                        Remove the equals option 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 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










                        answered 3 hours ago









                        jvdmrjvdmr

                        1784




                        1784























                            1














                            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


























                            • There is no t or getT() in this case, it's simply used as a label in this example. And it can't be used to order the Stream; it's the order of the items in the original list that is important.

                              – Druckles
                              3 hours ago
















                            1














                            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


























                            • There is no t or getT() in this case, it's simply used as a label in this example. And it can't be used to order the Stream; it's the order of the items in the original list that is important.

                              – Druckles
                              3 hours ago














                            1












                            1








                            1







                            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 2 hours ago

























                            answered 3 hours ago









                            Ravindra RanwalaRavindra Ranwala

                            9,24031635




                            9,24031635













                            • There is no t or getT() in this case, it's simply used as a label in this example. And it can't be used to order the Stream; it's the order of the items in the original list that is important.

                              – Druckles
                              3 hours ago



















                            • There is no t or getT() in this case, it's simply used as a label in this example. And it can't be used to order the Stream; it's the order of the items in the original list that is important.

                              – Druckles
                              3 hours ago

















                            There is no t or getT() in this case, it's simply used as a label in this example. And it can't be used to order the Stream; it's the order of the items in the original list that is important.

                            – Druckles
                            3 hours ago





                            There is no t or getT() in this case, it's simply used as a label in this example. And it can't be used to order the Stream; it's the order of the items in the original list that is important.

                            – Druckles
                            3 hours ago











                            0














                            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






























                              0














                              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




























                                0












                                0








                                0







                                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 1 hour ago

























                                answered 2 hours ago









                                davidxxxdavidxxx

                                65.8k66494




                                65.8k66494























                                    0















                                    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




























                                      0















                                      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


























                                        0












                                        0








                                        0








                                        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 22 mins ago









                                        tobias_ktobias_k

                                        57.7k969106




                                        57.7k969106























                                            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 1 min ago

























                                                answered 2 hours ago









                                                ETOETO

                                                2,495626




                                                2,495626






























                                                    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

                                                    Callistus I

                                                    Tabula Rosettana

                                                    How to label and detect the document text images