java stream find match or the last one?












21















How to find the first match or the last element in a list using java stream?



Which means if no element matches the condition,then return the last element.



eg:



OptionalInt i = IntStream.rangeClosed(1,5)
.filter(x-> x == 7)
.findFirst();
System.out.print(i.getAsInt());


What should I do to make it return 5;










share|improve this question





























    21















    How to find the first match or the last element in a list using java stream?



    Which means if no element matches the condition,then return the last element.



    eg:



    OptionalInt i = IntStream.rangeClosed(1,5)
    .filter(x-> x == 7)
    .findFirst();
    System.out.print(i.getAsInt());


    What should I do to make it return 5;










    share|improve this question



























      21












      21








      21


      1






      How to find the first match or the last element in a list using java stream?



      Which means if no element matches the condition,then return the last element.



      eg:



      OptionalInt i = IntStream.rangeClosed(1,5)
      .filter(x-> x == 7)
      .findFirst();
      System.out.print(i.getAsInt());


      What should I do to make it return 5;










      share|improve this question
















      How to find the first match or the last element in a list using java stream?



      Which means if no element matches the condition,then return the last element.



      eg:



      OptionalInt i = IntStream.rangeClosed(1,5)
      .filter(x-> x == 7)
      .findFirst();
      System.out.print(i.getAsInt());


      What should I do to make it return 5;







      java lambda java-8 java-stream






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Dec 6 '18 at 13:33









      Nicholas K

      6,19751031




      6,19751031










      asked Dec 6 '18 at 8:17









      金潇泽金潇泽

      1165




      1165
























          5 Answers
          5






          active

          oldest

          votes


















          14














          Given the list



          List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);


          You could just do :



          int value = list.stream().filter(x -> x == 2)
          .findFirst()
          .orElse(list.get(list.size() - 1));


          Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.



          If the list is empty you could return a default value, for example -1.



          int value = list.stream().filter(x -> x == 2)
          .findFirst()
          .orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));





          share|improve this answer

































            7














            You can use reduce() function like that:



            OptionalInt i = IntStream.rangeClosed(1, 5)
            .reduce((first, second) -> first == 7 ? first : second);
            System.out.print(i.getAsInt());





            share|improve this answer



















            • 1





              That's brilliant. You might want to use the reducing () collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.

              – daniu
              Dec 6 '18 at 9:15








            • 13





              @daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.

              – Eugene
              Dec 6 '18 at 9:39











            • @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.

              – statut
              Dec 6 '18 at 9:44








            • 5





              reduce is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods in Stream called findAny and findFirst to do this. Besides, if the stream was parallel, this wouldn't work, while findFirst would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...

              – Federico Peralta Schaffner
              Dec 6 '18 at 12:36








            • 4





              @FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.

              – Holger
              Dec 6 '18 at 13:43



















            5














            Basically I would use one of the following two methods or deviations thereof:



            Stream variant:



            <T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
            return list.stream()
            .filter(filter)
            .findFirst()
            .orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
            }


            non-stream variant:



            <T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
            T relevant = defaultValue;
            for (T entry : iterable) {
            relevant = entry;
            if (filter.test(entry))
            break;
            }
            return relevant;
            }


            Or as also Ilmari Karonen suggested in the comment with Iterable<T> you are then able to even call stream::iterator in case you really deal with a Stream instead of a List. Calling the shown methods would look as follows:



            getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
            getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
            getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
            // only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
            getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20


            I wouldn't use reduce here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse... (but that's probably just my opinion)



            I probably would then even end up with something as follows:



            <T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
            T relevant = null;
            for (T entry : iterable) {
            relevant = entry;
            if (filter.test(entry))
            break;
            }
            return Optional.ofNullable(relevant);
            }
            // or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...


            so that calls would rather look like:



            getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
            getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
            getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
            getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)





            share|improve this answer





















            • 2





              I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept any Iterable<T> instead of just a List<T>. That way, if you do in fact need to process a stream, you can just pass stream::iterator to this method.

              – Ilmari Karonen
              Dec 6 '18 at 19:03





















            3














            if you want to do this in one pipeline then you could do:



            int startInc = 1;
            int endEx = 5;
            OptionalInt first =
            IntStream.concat(IntStream.range(startInc, endEx)
            .filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
            .findFirst();


            but you're probably better off collecting the generated numbers into a list then operate on it as follows:



            // first collect the numbers into a list
            List<Integer> result = IntStream.rangeClosed(startInc,endEx)
            .boxed()
            .collect(toList());
            // then operate on it
            int value = result.stream()
            .filter(x -> x == 7)
            .findFirst()
            .orElse(result.get(result.size() - 1));


            Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:



            List<Integer> result = IntStream.rangeClosed(startInc,endEx)
            .boxed()
            .collect(toList());

            Optional<Integer> first =
            Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
            Stream.empty() : Stream.of(result.get(result.size() - 1)))
            .findFirst();





            share|improve this answer

































              2














              I am not sure why you really want to use streams for that, a simple for-loop would be enough:



              public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
              // handle empty case
              if(source.isEmpty()){
              return null;
              }
              for(T t : source){
              if(predicate.test(t)){
              return t;
              }
              }
              return source.get(source.size() -1);
              }


              Which then can be called like:



              Integer match = getFirstMatchingOrLast(ints, i -> i == 7);





              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%2f53647174%2fjava-stream-find-match-or-the-last-one%23new-answer', 'question_page');
                }
                );

                Post as a guest















                Required, but never shown

























                5 Answers
                5






                active

                oldest

                votes








                5 Answers
                5






                active

                oldest

                votes









                active

                oldest

                votes






                active

                oldest

                votes









                14














                Given the list



                List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);


                You could just do :



                int value = list.stream().filter(x -> x == 2)
                .findFirst()
                .orElse(list.get(list.size() - 1));


                Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.



                If the list is empty you could return a default value, for example -1.



                int value = list.stream().filter(x -> x == 2)
                .findFirst()
                .orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));





                share|improve this answer






























                  14














                  Given the list



                  List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);


                  You could just do :



                  int value = list.stream().filter(x -> x == 2)
                  .findFirst()
                  .orElse(list.get(list.size() - 1));


                  Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.



                  If the list is empty you could return a default value, for example -1.



                  int value = list.stream().filter(x -> x == 2)
                  .findFirst()
                  .orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));





                  share|improve this answer




























                    14












                    14








                    14







                    Given the list



                    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);


                    You could just do :



                    int value = list.stream().filter(x -> x == 2)
                    .findFirst()
                    .orElse(list.get(list.size() - 1));


                    Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.



                    If the list is empty you could return a default value, for example -1.



                    int value = list.stream().filter(x -> x == 2)
                    .findFirst()
                    .orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));





                    share|improve this answer















                    Given the list



                    List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);


                    You could just do :



                    int value = list.stream().filter(x -> x == 2)
                    .findFirst()
                    .orElse(list.get(list.size() - 1));


                    Here if the filter evaluates to true the element is retrieved, else the last element in the last is returned.



                    If the list is empty you could return a default value, for example -1.



                    int value = list.stream().filter(x -> x == 2)
                    .findFirst()
                    .orElse(list.isEmpty() ? -1 : list.get(list.size() - 1));






                    share|improve this answer














                    share|improve this answer



                    share|improve this answer








                    edited Dec 6 '18 at 14:06









                    John Kugelman

                    240k52399454




                    240k52399454










                    answered Dec 6 '18 at 8:58









                    Nicholas KNicholas K

                    6,19751031




                    6,19751031

























                        7














                        You can use reduce() function like that:



                        OptionalInt i = IntStream.rangeClosed(1, 5)
                        .reduce((first, second) -> first == 7 ? first : second);
                        System.out.print(i.getAsInt());





                        share|improve this answer



















                        • 1





                          That's brilliant. You might want to use the reducing () collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.

                          – daniu
                          Dec 6 '18 at 9:15








                        • 13





                          @daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.

                          – Eugene
                          Dec 6 '18 at 9:39











                        • @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.

                          – statut
                          Dec 6 '18 at 9:44








                        • 5





                          reduce is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods in Stream called findAny and findFirst to do this. Besides, if the stream was parallel, this wouldn't work, while findFirst would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...

                          – Federico Peralta Schaffner
                          Dec 6 '18 at 12:36








                        • 4





                          @FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.

                          – Holger
                          Dec 6 '18 at 13:43
















                        7














                        You can use reduce() function like that:



                        OptionalInt i = IntStream.rangeClosed(1, 5)
                        .reduce((first, second) -> first == 7 ? first : second);
                        System.out.print(i.getAsInt());





                        share|improve this answer



















                        • 1





                          That's brilliant. You might want to use the reducing () collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.

                          – daniu
                          Dec 6 '18 at 9:15








                        • 13





                          @daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.

                          – Eugene
                          Dec 6 '18 at 9:39











                        • @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.

                          – statut
                          Dec 6 '18 at 9:44








                        • 5





                          reduce is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods in Stream called findAny and findFirst to do this. Besides, if the stream was parallel, this wouldn't work, while findFirst would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...

                          – Federico Peralta Schaffner
                          Dec 6 '18 at 12:36








                        • 4





                          @FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.

                          – Holger
                          Dec 6 '18 at 13:43














                        7












                        7








                        7







                        You can use reduce() function like that:



                        OptionalInt i = IntStream.rangeClosed(1, 5)
                        .reduce((first, second) -> first == 7 ? first : second);
                        System.out.print(i.getAsInt());





                        share|improve this answer













                        You can use reduce() function like that:



                        OptionalInt i = IntStream.rangeClosed(1, 5)
                        .reduce((first, second) -> first == 7 ? first : second);
                        System.out.print(i.getAsInt());






                        share|improve this answer












                        share|improve this answer



                        share|improve this answer










                        answered Dec 6 '18 at 9:01









                        statutstatut

                        653412




                        653412








                        • 1





                          That's brilliant. You might want to use the reducing () collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.

                          – daniu
                          Dec 6 '18 at 9:15








                        • 13





                          @daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.

                          – Eugene
                          Dec 6 '18 at 9:39











                        • @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.

                          – statut
                          Dec 6 '18 at 9:44








                        • 5





                          reduce is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods in Stream called findAny and findFirst to do this. Besides, if the stream was parallel, this wouldn't work, while findFirst would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...

                          – Federico Peralta Schaffner
                          Dec 6 '18 at 12:36








                        • 4





                          @FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.

                          – Holger
                          Dec 6 '18 at 13:43














                        • 1





                          That's brilliant. You might want to use the reducing () collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.

                          – daniu
                          Dec 6 '18 at 9:15








                        • 13





                          @daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.

                          – Eugene
                          Dec 6 '18 at 9:39











                        • @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.

                          – statut
                          Dec 6 '18 at 9:44








                        • 5





                          reduce is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods in Stream called findAny and findFirst to do this. Besides, if the stream was parallel, this wouldn't work, while findFirst would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...

                          – Federico Peralta Schaffner
                          Dec 6 '18 at 12:36








                        • 4





                          @FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.

                          – Holger
                          Dec 6 '18 at 13:43








                        1




                        1





                        That's brilliant. You might want to use the reducing () collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.

                        – daniu
                        Dec 6 '18 at 9:15







                        That's brilliant. You might want to use the reducing () collector though, which allows you to give an identity element, so you're able to handle empty and one element streams.

                        – daniu
                        Dec 6 '18 at 9:15






                        13




                        13





                        @daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.

                        – Eugene
                        Dec 6 '18 at 9:39





                        @daniu the only problem is that this is not short-circuiting, so if your first match happens to be on the first element, you would still have to traverse the entire stream's source, even if you know the result already.

                        – Eugene
                        Dec 6 '18 at 9:39













                        @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.

                        – statut
                        Dec 6 '18 at 9:44







                        @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.

                        – statut
                        Dec 6 '18 at 9:44






                        5




                        5





                        reduce is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods in Stream called findAny and findFirst to do this. Besides, if the stream was parallel, this wouldn't work, while findFirst would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...

                        – Federico Peralta Schaffner
                        Dec 6 '18 at 12:36







                        reduce is not meant to perform searches, but associative operations instead, i.e. sum of elements. There are methods in Stream called findAny and findFirst to do this. Besides, if the stream was parallel, this wouldn't work, while findFirst would do the exact job, despite the characteristics of the stream. As others have stated, this doesn't short-circuit either...

                        – Federico Peralta Schaffner
                        Dec 6 '18 at 12:36






                        4




                        4





                        @FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.

                        – Holger
                        Dec 6 '18 at 13:43





                        @FedericoPeraltaSchaffner this function is associative, so it wouldn’t have any problems with parallel execution. So the only problem is that it is inefficient.

                        – Holger
                        Dec 6 '18 at 13:43











                        5














                        Basically I would use one of the following two methods or deviations thereof:



                        Stream variant:



                        <T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
                        return list.stream()
                        .filter(filter)
                        .findFirst()
                        .orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
                        }


                        non-stream variant:



                        <T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
                        T relevant = defaultValue;
                        for (T entry : iterable) {
                        relevant = entry;
                        if (filter.test(entry))
                        break;
                        }
                        return relevant;
                        }


                        Or as also Ilmari Karonen suggested in the comment with Iterable<T> you are then able to even call stream::iterator in case you really deal with a Stream instead of a List. Calling the shown methods would look as follows:



                        getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
                        getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
                        getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
                        // only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
                        getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20


                        I wouldn't use reduce here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse... (but that's probably just my opinion)



                        I probably would then even end up with something as follows:



                        <T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
                        T relevant = null;
                        for (T entry : iterable) {
                        relevant = entry;
                        if (filter.test(entry))
                        break;
                        }
                        return Optional.ofNullable(relevant);
                        }
                        // or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...


                        so that calls would rather look like:



                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
                        getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)





                        share|improve this answer





















                        • 2





                          I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept any Iterable<T> instead of just a List<T>. That way, if you do in fact need to process a stream, you can just pass stream::iterator to this method.

                          – Ilmari Karonen
                          Dec 6 '18 at 19:03


















                        5














                        Basically I would use one of the following two methods or deviations thereof:



                        Stream variant:



                        <T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
                        return list.stream()
                        .filter(filter)
                        .findFirst()
                        .orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
                        }


                        non-stream variant:



                        <T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
                        T relevant = defaultValue;
                        for (T entry : iterable) {
                        relevant = entry;
                        if (filter.test(entry))
                        break;
                        }
                        return relevant;
                        }


                        Or as also Ilmari Karonen suggested in the comment with Iterable<T> you are then able to even call stream::iterator in case you really deal with a Stream instead of a List. Calling the shown methods would look as follows:



                        getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
                        getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
                        getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
                        // only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
                        getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20


                        I wouldn't use reduce here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse... (but that's probably just my opinion)



                        I probably would then even end up with something as follows:



                        <T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
                        T relevant = null;
                        for (T entry : iterable) {
                        relevant = entry;
                        if (filter.test(entry))
                        break;
                        }
                        return Optional.ofNullable(relevant);
                        }
                        // or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...


                        so that calls would rather look like:



                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
                        getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)





                        share|improve this answer





















                        • 2





                          I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept any Iterable<T> instead of just a List<T>. That way, if you do in fact need to process a stream, you can just pass stream::iterator to this method.

                          – Ilmari Karonen
                          Dec 6 '18 at 19:03
















                        5












                        5








                        5







                        Basically I would use one of the following two methods or deviations thereof:



                        Stream variant:



                        <T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
                        return list.stream()
                        .filter(filter)
                        .findFirst()
                        .orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
                        }


                        non-stream variant:



                        <T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
                        T relevant = defaultValue;
                        for (T entry : iterable) {
                        relevant = entry;
                        if (filter.test(entry))
                        break;
                        }
                        return relevant;
                        }


                        Or as also Ilmari Karonen suggested in the comment with Iterable<T> you are then able to even call stream::iterator in case you really deal with a Stream instead of a List. Calling the shown methods would look as follows:



                        getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
                        getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
                        getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
                        // only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
                        getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20


                        I wouldn't use reduce here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse... (but that's probably just my opinion)



                        I probably would then even end up with something as follows:



                        <T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
                        T relevant = null;
                        for (T entry : iterable) {
                        relevant = entry;
                        if (filter.test(entry))
                        break;
                        }
                        return Optional.ofNullable(relevant);
                        }
                        // or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...


                        so that calls would rather look like:



                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
                        getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)





                        share|improve this answer















                        Basically I would use one of the following two methods or deviations thereof:



                        Stream variant:



                        <T> T getFirstMatchOrLast(List<T> list, Predicate<T> filter, T defaultValue) {
                        return list.stream()
                        .filter(filter)
                        .findFirst()
                        .orElse(list.isEmpty() ? defaultValue : list.get(list.size() - 1));
                        }


                        non-stream variant:



                        <T> T getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter, T defaultValue) {
                        T relevant = defaultValue;
                        for (T entry : iterable) {
                        relevant = entry;
                        if (filter.test(entry))
                        break;
                        }
                        return relevant;
                        }


                        Or as also Ilmari Karonen suggested in the comment with Iterable<T> you are then able to even call stream::iterator in case you really deal with a Stream instead of a List. Calling the shown methods would look as follows:



                        getFirstMatchOrLast(Arrays.asList(1, 20, 3), i -> i == 20, 1); // returns 20
                        getFirstMatchOrLast(Collections.emptyList(), i -> i == 3, 20); // returns 20
                        getFirstMatchOrLast(Arrays.asList(1, 2, 20), i -> i == 7, 30); // returns 20
                        // only non-stream variant: having a Stream<Integer> stream = Stream.of(1, 2, 20)
                        getFirstMatchOrLast(stream::iterator, i -> i == 7, 30); // returns 20


                        I wouldn't use reduce here because it sounds wrong to me in the sense, that it also goes through the whole entries even though the first entry could have matched already, i.e. it doesn't short-circuit anymore. Moreover for me it isn't as readable as filter.findFirst.orElse... (but that's probably just my opinion)



                        I probably would then even end up with something as follows:



                        <T> Optional<T> getFirstMatchOrLast(Iterable<T> iterable, Predicate<T> filter) {
                        T relevant = null;
                        for (T entry : iterable) {
                        relevant = entry;
                        if (filter.test(entry))
                        break;
                        }
                        return Optional.ofNullable(relevant);
                        }
                        // or transform the stream variant to somethinng like that... however I think that isn't as readable anymore...


                        so that calls would rather look like:



                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseThrow(...)
                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElse(0);
                        getFirstMatchOrLast(Arrays.asList(1, 2, 3, 5), i -> i == 7).orElseGet(() -> /* complex formula */);
                        getFirstMatchOrLast(stream::iterator, i -> i == 5).ifPresent(...)






                        share|improve this answer














                        share|improve this answer



                        share|improve this answer








                        edited Dec 10 '18 at 15:38

























                        answered Dec 6 '18 at 10:31









                        RolandRoland

                        9,49711141




                        9,49711141








                        • 2





                          I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept any Iterable<T> instead of just a List<T>. That way, if you do in fact need to process a stream, you can just pass stream::iterator to this method.

                          – Ilmari Karonen
                          Dec 6 '18 at 19:03
















                        • 2





                          I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept any Iterable<T> instead of just a List<T>. That way, if you do in fact need to process a stream, you can just pass stream::iterator to this method.

                          – Ilmari Karonen
                          Dec 6 '18 at 19:03










                        2




                        2





                        I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept any Iterable<T> instead of just a List<T>. That way, if you do in fact need to process a stream, you can just pass stream::iterator to this method.

                        – Ilmari Karonen
                        Dec 6 '18 at 19:03







                        I like your "non-stream variant" (with or without Optional), but I'd suggest generalizing it to accept any Iterable<T> instead of just a List<T>. That way, if you do in fact need to process a stream, you can just pass stream::iterator to this method.

                        – Ilmari Karonen
                        Dec 6 '18 at 19:03













                        3














                        if you want to do this in one pipeline then you could do:



                        int startInc = 1;
                        int endEx = 5;
                        OptionalInt first =
                        IntStream.concat(IntStream.range(startInc, endEx)
                        .filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
                        .findFirst();


                        but you're probably better off collecting the generated numbers into a list then operate on it as follows:



                        // first collect the numbers into a list
                        List<Integer> result = IntStream.rangeClosed(startInc,endEx)
                        .boxed()
                        .collect(toList());
                        // then operate on it
                        int value = result.stream()
                        .filter(x -> x == 7)
                        .findFirst()
                        .orElse(result.get(result.size() - 1));


                        Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:



                        List<Integer> result = IntStream.rangeClosed(startInc,endEx)
                        .boxed()
                        .collect(toList());

                        Optional<Integer> first =
                        Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
                        Stream.empty() : Stream.of(result.get(result.size() - 1)))
                        .findFirst();





                        share|improve this answer






























                          3














                          if you want to do this in one pipeline then you could do:



                          int startInc = 1;
                          int endEx = 5;
                          OptionalInt first =
                          IntStream.concat(IntStream.range(startInc, endEx)
                          .filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
                          .findFirst();


                          but you're probably better off collecting the generated numbers into a list then operate on it as follows:



                          // first collect the numbers into a list
                          List<Integer> result = IntStream.rangeClosed(startInc,endEx)
                          .boxed()
                          .collect(toList());
                          // then operate on it
                          int value = result.stream()
                          .filter(x -> x == 7)
                          .findFirst()
                          .orElse(result.get(result.size() - 1));


                          Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:



                          List<Integer> result = IntStream.rangeClosed(startInc,endEx)
                          .boxed()
                          .collect(toList());

                          Optional<Integer> first =
                          Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
                          Stream.empty() : Stream.of(result.get(result.size() - 1)))
                          .findFirst();





                          share|improve this answer




























                            3












                            3








                            3







                            if you want to do this in one pipeline then you could do:



                            int startInc = 1;
                            int endEx = 5;
                            OptionalInt first =
                            IntStream.concat(IntStream.range(startInc, endEx)
                            .filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
                            .findFirst();


                            but you're probably better off collecting the generated numbers into a list then operate on it as follows:



                            // first collect the numbers into a list
                            List<Integer> result = IntStream.rangeClosed(startInc,endEx)
                            .boxed()
                            .collect(toList());
                            // then operate on it
                            int value = result.stream()
                            .filter(x -> x == 7)
                            .findFirst()
                            .orElse(result.get(result.size() - 1));


                            Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:



                            List<Integer> result = IntStream.rangeClosed(startInc,endEx)
                            .boxed()
                            .collect(toList());

                            Optional<Integer> first =
                            Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
                            Stream.empty() : Stream.of(result.get(result.size() - 1)))
                            .findFirst();





                            share|improve this answer















                            if you want to do this in one pipeline then you could do:



                            int startInc = 1;
                            int endEx = 5;
                            OptionalInt first =
                            IntStream.concat(IntStream.range(startInc, endEx)
                            .filter(x -> x == 7), endEx > 1 ? IntStream.of(endEx) : IntStream.empty())
                            .findFirst();


                            but you're probably better off collecting the generated numbers into a list then operate on it as follows:



                            // first collect the numbers into a list
                            List<Integer> result = IntStream.rangeClosed(startInc,endEx)
                            .boxed()
                            .collect(toList());
                            // then operate on it
                            int value = result.stream()
                            .filter(x -> x == 7)
                            .findFirst()
                            .orElse(result.get(result.size() - 1));


                            Alternatively, if you want to make the latter return an empty Optional in the case of the source being empty (if that's a possible scenario) instead of an exception then you could do:



                            List<Integer> result = IntStream.rangeClosed(startInc,endEx)
                            .boxed()
                            .collect(toList());

                            Optional<Integer> first =
                            Stream.concat(result.stream().filter(x -> x == 7), result.isEmpty() ?
                            Stream.empty() : Stream.of(result.get(result.size() - 1)))
                            .findFirst();






                            share|improve this answer














                            share|improve this answer



                            share|improve this answer








                            edited Dec 6 '18 at 8:56

























                            answered Dec 6 '18 at 8:41









                            AomineAomine

                            41.8k74071




                            41.8k74071























                                2














                                I am not sure why you really want to use streams for that, a simple for-loop would be enough:



                                public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
                                // handle empty case
                                if(source.isEmpty()){
                                return null;
                                }
                                for(T t : source){
                                if(predicate.test(t)){
                                return t;
                                }
                                }
                                return source.get(source.size() -1);
                                }


                                Which then can be called like:



                                Integer match = getFirstMatchingOrLast(ints, i -> i == 7);





                                share|improve this answer




























                                  2














                                  I am not sure why you really want to use streams for that, a simple for-loop would be enough:



                                  public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
                                  // handle empty case
                                  if(source.isEmpty()){
                                  return null;
                                  }
                                  for(T t : source){
                                  if(predicate.test(t)){
                                  return t;
                                  }
                                  }
                                  return source.get(source.size() -1);
                                  }


                                  Which then can be called like:



                                  Integer match = getFirstMatchingOrLast(ints, i -> i == 7);





                                  share|improve this answer


























                                    2












                                    2








                                    2







                                    I am not sure why you really want to use streams for that, a simple for-loop would be enough:



                                    public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
                                    // handle empty case
                                    if(source.isEmpty()){
                                    return null;
                                    }
                                    for(T t : source){
                                    if(predicate.test(t)){
                                    return t;
                                    }
                                    }
                                    return source.get(source.size() -1);
                                    }


                                    Which then can be called like:



                                    Integer match = getFirstMatchingOrLast(ints, i -> i == 7);





                                    share|improve this answer













                                    I am not sure why you really want to use streams for that, a simple for-loop would be enough:



                                    public static <T> T getFirstMatchingOrLast(List<? extends T> source, Predicate<? super T> predicate){
                                    // handle empty case
                                    if(source.isEmpty()){
                                    return null;
                                    }
                                    for(T t : source){
                                    if(predicate.test(t)){
                                    return t;
                                    }
                                    }
                                    return source.get(source.size() -1);
                                    }


                                    Which then can be called like:



                                    Integer match = getFirstMatchingOrLast(ints, i -> i == 7);






                                    share|improve this answer












                                    share|improve this answer



                                    share|improve this answer










                                    answered Dec 6 '18 at 10:09









                                    LinoLino

                                    7,42221936




                                    7,42221936






























                                        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%2f53647174%2fjava-stream-find-match-or-the-last-one%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

                                        Berounka

                                        Fiat S.p.A.

                                        Type 'String' is not a subtype of type 'int' of 'index'