Think different and look outside. Here is one of the alternative solutions by StreamEx, and you may not accept it by your declaration:
String str = "ddaaaacccjcccccjjj";
IntStreamEx.range(0, str.length()).boxed()
.collapse((i, j) -> str.charAt(i) == str.charAt(j), Collectors.toList())
.maxBy(l -> l.size())
.map(l -> Triple.of(l.get(0), l.size(), str.charAt(l.get(0))))
.ifPresent(System.out::println);
// output: [10, 5, c]
And to get all:
String str = "ddaaacccaaa";
IntStreamEx.range(0, str.length()).boxed()
.collapse((i, j) -> str.charAt(i) == str.charAt(j), Collectors.toList())
.collect(MoreCollectors.maxAll(Comparators.comparingBy(l -> l.size())))
.stream().map(l -> Triple.of(l.get(0), l.size(), str.charAt(l.get(0))))
.forEach(System.out::println);
// output
// [2, 3, a]
// [5, 3, c]
// [8, 3, a]
To distinct the result by character:
Collector<List<Integer>, ?, StreamEx<List<Integer>>> collector = Collectors.collectingAndThen(
MoreCollectors.maxAll(Comparators.comparingBy(l -> l.size())), StreamEx::of);
IntStreamEx.range(0, str.length()).boxed()
.collapse((i, j) -> str.charAt(i) == str.charAt(j), Collectors.toList())
.collect(collector)
.distinct(l -> str.charAt(l.get(0)))
.map(l -> Triple.of(l.get(0), l.size(), str.charAt(l.get(0))))
.forEach(System.out::println);
// output
// [2, 3, a]
// [5, 3, c]
Update:
Is it good enough? actually no, because it creates unnecessary temporary List
. I think there is a better solution by intervalMap
.
IntStreamEx.range(0, str.length()).boxed()
.intervalMap((i, j) -> str.charAt(i) == str.charAt(j), Pair::of)
.maxBy(p -> p.right - p.left)
.map(p -> Triple.of(p.left, p.right - p.left + 1, str.charAt(p.left)))
.ifPresent(System.out::println);