Setting boolean flag inside Java 8 Stream

I am wondering what is the best fit for setting a boolean flag value from a java stream. Here's an example of what I want to do:

    List<Integer> list = Arrays.asList(1,2,3,4,5);
    boolean flag = false;
    List<Integer> newList = list.stream()
                                //many other filters, flatmaps, etc...
                                .filter(i -> {
                                    if(condition(i)){
                                        flag = true;
                                    }
                                    return condition(i);
                                })
                                //many other filters, flatmaps, etc...
                                .collect(Collectors.toList());
    //do some work with the new list and the flag

      

However, this is contrary to language restrictions. "The variable used in the lambda expression must be final or effective final." There are several solutions I can think of, but I'm not sure which is better. This first solution I have is to add items matching condition

, list and check List::isEmpty

. Can also be wrapped flag

in AtomicReference

.

Please note that my question is similar to this question , but I am trying to fetch a boolean at the end rather than setting a variable.

+3


source to share


3 answers


Do not close a create task newList

with a completely unrelated task. Just use

boolean flag = list.stream().anyMatch(i -> condition(i));

      

followed by another stream code.

There are two typical objections

  • But this is repeated twice.

    Yes it is, but who says the repetition ArrayList

    problem is a problem? Don't try to avoid multiple stream operations unless you know you actually have a stream source with an expensive stream, such as an external file. If you have such an expensive source, it's still easier to collect the items in the collection in the first place, which you can go through twice.

  • But his score is condition(โ€ฆ)

    more than once.

    Well, actually his score is less than the original code

    .filter(i -> {
        if(condition(i)){
            flag = true;
        }
        return condition(i);
    })
    
          

    how it anyMatch

    stops at the first match, while the original predicate evaluates condition(i)

    twice per element, unconditionally.


If you have multiple intermediate steps preceding this condition, you can collect the intermediate List

, for example

List<Integer> intermediate = list.stream()
    //many other filters, flatmaps, etc...
    .filter(i -> condition(i))
    .collect(Collectors.toList());
boolean flag = !intermediate.isEmpty();
List<Integer> newList = intermediate.stream()
    //many other filters, flatmaps, etc...
    .collect(Collectors.toList());

      



but more often than it might seem at first glance an intermediate step, how expensive it is on it. The performance of similar intermediate steps may differ from one line to another depending on the actual operation of the terminal. Thus, it can still perform these actions quite efficiently on the fly:

boolean flag = list.stream()
    //many other filters, flatmaps, etc...
    .anyMatch(i -> condition(i));
List<Integer> newList = list.stream()
    //many other filters, flatmaps, etc...
    .filter(i -> condition(i))
    //many other filters, flatmaps, etc...
    .collect(Collectors.toList());

      

If you are worried about the code duplication itself, you can still put the generic code on the stream that returns the utility method.

Only on very rare occasions, it pays to go to the lowlevel API and look into the Stream like in this answer . And if you do, you shouldn't go down a route Iterator

that will lose meta information about the content, but use Spliterator

:

Spliterator<Integer> sp = list.stream()
    //many other filters, flatmaps, etc...
    .filter(i -> condition(i))
    .spliterator();
Stream.Builder<Integer> first = Stream.builder();
boolean flag = sp.tryAdvance(first);
List<Integer> newList = Stream.concat(first.build(), StreamSupport.stream(sp, false))
    //many other filters, flatmaps, etc...
    .collect(Collectors.toList());

      

Note that in all of these cases, you can shorten if flag

- false

, as the result can only be an empty list:

List<Integer> newList = !flag? Collections.emptyList():
/*
   subsequent stream operation
 */;

      

+8


source


EDIT: (based on Holger's comment below )

I'm only allowing this answer for historical purposes;) This was my attempt at solving the problem with help Iterator

, although Spliterator

much better. This answer is not 100% wrong, however the characteristics of the splitter that the stream supports (i.e. SIZED

, ORDERED

etc.) are lost when the stream is converted to Iterator

. Please see Holger's awesome answer for a better approach if there are other alternatives and a short discussion on whether it's worth doing.


If you need to know if a filter condition was matched in the middle of a stream pipeline, you may need to convert the stream to Iterator

, check if the iterator has the next element, stores that value as your flag, then create a new stream from the iterator, and finally continue with flow conveyor.

In code:

Iterator<Whatever> iterator = list.stream()
    // many other filters, flatmaps, etc...
    .filter(i -> condition(i))
    .iterator();

boolean flag = iterator.hasNext();

      



Then create a new one Stream

from the iterator:

Stream<Whatever> stream = StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(
        iterator, 
        Spliterator.NONNULL), // maybe Spliterator.ORDERED?
    false);

      

Finally, continue with the streaming pipeline:

List<Integer> newList = stream
    // many other filters, flatmaps, etc...
    .collect(Collectors.toList());

      

Now you have newList

and flag

to work with.

+2


source


Check boolean checkbox separately

List<Integer> list = Arrays.asList(1,2,3,4,5);
List<Integer> newList = list.stream()
                            .filter(i -> condition(i))
                            //many other filters, flatmaps, etc...
                            .collect(Collectors.toList());

boolean flag = list.stream()
                     .filter(i -> condition(i))
                     .findAny()
                     .isPresent();

      

+1


source







All Articles