Why do I need to map IntStream to Stream<Character>
The method CharSequence::chars
returns the IntStream
, which of course doesn't provide any method converting to int, such as mapToInt
, but mapToObj
instead. Therefore the method IntStream::map(IntUnaryOperator mapper)
which both takes returns int
as well shall be used since IntUnaryOperator
does the same like Function<Integer, Integer>
or UnaryOperator<Integer>
:
int count = myString.chars() // IntStream
.map(c -> (set.add((char) c) ? 1 : 0)) // IntStream
.sum();
long count = myString.chars() // IntStream
.filter(c -> set.add((char) c)) // IntStream
.count();
Also, using Set<Integer>
helps you to avoid conversion to a Character:
Set<Integer> set = new HashSet<>();
int count = myString.chars() // IntStream
.map(c -> (set.add(c) ? 1 : 0)) // IntStream
.sum();
long count = myString.chars() // IntStream
.filter(set::add) // IntStream
.count();
However, regardless of what you try to achieve, your code is wrong by principle, the NO Stateless behaviors to be exact. Consider using the following snippet which lambda expressions' results are not dependent on the result of a non-deterministic operation, such as Set::add
.
Stream pipeline results may be nondeterministic or incorrect if the behavioral parameters to the stream operations are stateful.
long count = myString.chars() // IntStream
.distinct() // IntStream
.count();
You can also collect to a set and then take the size without using an explicit map. It does not require using external state to contain the characters.
long count = str.chars().boxed().collect(Collectors.toSet()).size();
But imho, the more direct approach which was already mentioned is cleaner in appearance and the one I would prefer to use.
long count = str.chars().distinct().count();