java stream find match or the last one?











up vote
20
down vote

favorite
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




























    up vote
    20
    down vote

    favorite
    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


























      up vote
      20
      down vote

      favorite
      1









      up vote
      20
      down vote

      favorite
      1






      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 at 13:33









      Nicholas K

      4,86641031




      4,86641031










      asked Dec 6 at 8:17









      金潇泽

      1115




      1115
























          5 Answers
          5






          active

          oldest

          votes

















          up vote
          13
          down vote



          accepted










          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






























            up vote
            7
            down vote













            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 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 at 9:39










            • @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
              – statut
              Dec 6 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 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 at 13:43


















            up vote
            5
            down vote













            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 at 19:03




















            up vote
            3
            down vote













            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






























              up vote
              2
              down vote













              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',
                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








                up vote
                13
                down vote



                accepted










                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



























                  up vote
                  13
                  down vote



                  accepted










                  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

























                    up vote
                    13
                    down vote



                    accepted







                    up vote
                    13
                    down vote



                    accepted






                    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 at 14:06









                    John Kugelman

                    239k51397451




                    239k51397451










                    answered Dec 6 at 8:58









                    Nicholas K

                    4,86641031




                    4,86641031
























                        up vote
                        7
                        down vote













                        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 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 at 9:39










                        • @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
                          – statut
                          Dec 6 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 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 at 13:43















                        up vote
                        7
                        down vote













                        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 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 at 9:39










                        • @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
                          – statut
                          Dec 6 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 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 at 13:43













                        up vote
                        7
                        down vote










                        up vote
                        7
                        down vote









                        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 at 9:01









                        statut

                        643412




                        643412








                        • 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 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 at 9:39










                        • @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
                          – statut
                          Dec 6 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 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 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 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 at 9:39










                        • @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
                          – statut
                          Dec 6 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 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 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 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 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 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 at 9:39












                        @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
                        – statut
                        Dec 6 at 9:44






                        @Eugene totally agree. Basically I would use LinkedHashSet to check element existence and return last element otherwise.
                        – statut
                        Dec 6 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 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 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 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 at 13:43










                        up vote
                        5
                        down vote













                        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 at 19:03

















                        up vote
                        5
                        down vote













                        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 at 19:03















                        up vote
                        5
                        down vote










                        up vote
                        5
                        down vote









                        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 at 15:38

























                        answered Dec 6 at 10:31









                        Roland

                        9,22711141




                        9,22711141








                        • 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 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 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 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 at 19:03












                        up vote
                        3
                        down vote













                        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



























                          up vote
                          3
                          down vote













                          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

























                            up vote
                            3
                            down vote










                            up vote
                            3
                            down vote









                            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 at 8:56

























                            answered Dec 6 at 8:41









                            Aomine

                            36.1k62961




                            36.1k62961






















                                up vote
                                2
                                down vote













                                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

























                                  up vote
                                  2
                                  down vote













                                  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























                                    up vote
                                    2
                                    down vote










                                    up vote
                                    2
                                    down vote









                                    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 at 10:09









                                    Lino

                                    6,98221936




                                    6,98221936






























                                        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.





                                        Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                                        Please pay close attention to the following guidance:


                                        • 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

                                        Список кардиналов, возведённых папой римским Каликстом III

                                        Deduzione

                                        Mysql.sock missing - “Can't connect to local MySQL server through socket”