Reputation: 464
I used the below example code only to illustrate the problem with Java 8 stream API. I am not expecting workaround to the given code but the explanation why accumulator function is not possible/provided in Stream.
I want to get the first recurring character in a string without using the intermediate terminal operation or shared mutation using Java 8 Stream api. I can do this with RxJava scan operator without pain. The Scan/accumulator basically enable me to get hold of the previous element in the stream and I would be able to return my own type as the previous element from the scan. I see there are no possibilities with Java 8 stream but wanted to know why? What is the problem in implementing the scan/accumulator operation in Stream?
Issues with the workaround to achieve this:
If I use the terminal operation then all of my inputs are processed which is unnecessary
If I use mutation the code cannot be parallelized or bring more issues if someone uses stream.parallel.
given - String str = "rtydydret";
output - y - because y is the first repeating character in the string.
Imperative example to achieve this: -
List<Character> list = new ArrayList<>();
for (char charecter : str.toCharArray()) {
if (list.contains(charecter)) {
System.out.println(charecter);
break;
} else {
list.add(charecter);
}
}
I also do not want to use below code as it uses terminal operation which means it processed all the character in the string, that's bad.
Map<Character, Long> collect = "abcsdnvs".chars().mapToObj(i -> (char)i).collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()));
collect.forEach( (x,y) -> System.out.println( "Key: " + x + " Val: " + y));
Optional<Character> firstRepeat = collect.entrySet().stream().filter( (e) -> e.getValue() > 1).map(e -> e.getKey()).findFirst();
System.out.println("First repeating:" + firstRepeat.orElse(null));
Any insight would be greatly helpful. Thanks.
Upvotes: 0
Views: 1874
Reputation: 18235
I see there are no possibilities with Java 8 stream but wanted to know why?
I don't think you can achieve the result with parallelism, because you cannot determine which one come first when parallel processing unless you process all of your characters. (as you need to compare index of all result from parallel, if any, to determine which one comes first)
Without parallel-ready restriction (do not use parallel with this code), you can try this:
Set<Integer> filter = new HashSet<>();
OptionalInt c = str.chars().filter(e -> !filter.add(e)).findFirst();
if (c.isPresent()) {
// You can access your repeated char here
(char) c.getAsInt();
}
Upvotes: 1
Reputation: 120968
There are a couple of ways. The first one (which I would suggest is using a regex):
String input = "abcfra";
Pattern p = Pattern.compile("(\\w)(?=.*\\1)");
Matcher m = p.matcher("input");
if (m.find()) {
System.out.println(m.group());
}
The second one is a Stream-way of two loops actually:
int index = IntStream.range(0, input.length())
.filter(x -> IntStream.range(x, input.length())
.filter(y -> input.indexOf(x) == input.indexOf(y))
.findAny()
.isPresent()
)
.findFirst()
.getAsInt();
System.out.println(input.charAt(index));
And the third one, which you apparently don't like would be:
char result = input.chars()
.collect(
() -> new LinkedHashMap<Character, Integer>(),
(map, x) -> map.merge((char) x, 1, (oldV, newV) -> oldV + newV),
(left, right) -> {
for (Entry<Character, Integer> e : right.entrySet()) {
left.merge(e.getKey(), e.getValue(), (oldV, newV) -> oldV + newV);
}
}
)
.entrySet()
.stream()
.filter(x -> x.getValue() > 1)
.map(Entry::getKey)
.findFirst()
.orElse('?');
Which is basically close to the same way that you did. Just notice that you are trying to solve a problem that probably does not need solving to begin with (the performance one, going through all of the input and collecting to a Map
).
Upvotes: 1
Reputation: 2490
I see there are no possibilities with Java 8 stream but wanted to know why?
Because that would make the language, specifically the part with streams, and map/filter/reduce, more complicated than what it is now.
And for nothing, considering that you can implement that without a problem, without using streams. It's a tradeoff between complexity of the language and convenience it brings or doesn't bring.
Upvotes: 1