How to validate elements produced during a Java Stream before Collect?

Asked

Viewed 250 times

1

Given the hypothetical example below as it would be the best way to validate all the elements produced by a Java Stream, in the example below I would like to check if all the elements produced are pairs or throw an error.

Set result = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream()
    .filter( n -> n % 2 == 0)
    //Validar aqui se todos são pares ou lançar uma Exception
    .map(String::valueOf)
    .collect(Collectors.toSet())
  • But the filter have you only taken even numbers, so why check again if all are even? Or you want to throw an exception if you have no pair?

  • It is a hypothetical example, the validation could be if all pairs are less than or equal to 10, the objective and make a validation whatever.

  • Maybe use allMatch solve. The problem is that this is a terminal operation and cannot be used collect after. It may be simpler to include all the filter and at the end check whether the Set is empty, for example.

2 answers

2

Taking the second solution from reply by @Scarabelo, but adapting to something more suitable.

What is desired is not to make a filter, but a bizoiada (word from neoclassical cearencês). This look is made on each element of the element, and it is passed on in the flow of stream. Is the method Stream<T>.peek(Consumer<? super T>). With it, you can simply run without worrying about return.

In his example:

Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream()
  .peek(n -> {
    if (n % 2 != 0) {
      throw new RunTimeException();
    }
  })
  .map(String::valueOf)
  .collect(Collectors.toSet());

You can put the peek() at any time prior to the terminal operation of the Stream, since it is an intermediary operation.


About the first option of reply, I am not favorable to her for two reasons:

  1. to Stream may not be "rewindeable" (example)
  2. will always go through the collection of elements twice

The second solution uses a larger amount of memory in case of failures, but processing is done only once.

2


A relatively elegant solution would be to use anymatch to check if there is any scenario where you should launch Exception and in a second moment, if there was no Exception, you would follow with your processing normally, not needing capture after after it has been validated that your data are consistent.

        Set<Integer> result = (Set<Integer>) Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

        if(result.stream().anyMatch(n -> n % 2 != 0)){
            throw new Exception();
        }

        {seu código}

Considering the fact that the lambda does not run exactly at the place where it was written, but somewhere "magical" and unrelated from your jdk that location would be where your Exception would be checked, not interrupting execution. To solve this we must create a wrapper that aims to translate a checked Exception to a not checked and "pop" the failure.

    public class Main {
        public static void main(String[] args) {
            Set result = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream()
                    .filter(n ->uncheckCall(() -> {
                        if(n % 2 == 0){
                            return true;
                        }else{
                            throw new Exception();
                        }
                    }))
                    .map(String::valueOf)
                    .collect(Collectors.toSet());

            System.out.println(result);
        }

        public static <T> T uncheckCall(Callable<T> callable) {
            try {
                return callable.call();
            }catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

Personally I prefer the first solution, but there is a second possibility.

  • In my scenario the second option fits better

  • I think it’s more appropriate to use the peek in place of filter. I put this in more detail in my reply

Browser other questions tagged

You are not signed in. Login or sign up in order to post.