PS I can’t sort by normal to keep the order as when I entered (aaabccccCCaB), and my stream counts all the same emails, i.e. produces a trace of the result: 4aBb2C4c instead of 3ab4c2CaB.

public class Main { public static void main(String[] args) { Arrays.stream((new Scanner(System.in)).nextLine().split("")) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) .forEach((el, count) -> { if (count>1) System.out.print(count + el); if (count<=1) System.out.print(el); }); } } 
  • And why do you do this with the help of Stream? The usual cycle using indexes is done simply and clearly. - Regent
  • I have such a task - to solve with the help of stream, with the help of cycles, I think I can handle it ... - Victor Kondratyuk
  • This is called RLE . - D-side

3 answers 3

You can, for example, like this:

 String text = "aaabccccCCaB"; Arrays.stream(text.split("")) .collect(Collector.of(ArrayList<StringBuilder>::new, (o, s) -> { if (o.size() != 0 && o.get(o.size()-1).charAt(0) == s.charAt(0)) { StringBuilder sb = o.get(o.size() - 1); sb.append(s); } else { o.add(new StringBuilder(s)); } }, (o, o2) -> null)) .forEach(sb -> System.out.print((sb.length()>1 ? sb.length() : "") + sb.substring(0,1))); 

PS Will not work on parallel streams due to:

 (o, o2) -> null) 

Also with the imposition of BiConsumer:

 BiConsumer<List<StringBuilder>, String> biConsumer = (list, s) -> { if (list.size() != 0 && (list.get(list.size() - 1)).charAt(0) == s.charAt(0)) { StringBuilder sb = list.get(list.size() - 1); sb.append(s); } else { list.add(new StringBuilder(s)); } }; System.out.println(Arrays.stream(text.split("")) .collect(Collector.of(ArrayList::new, biConsumer, (o, o2) -> null)) .stream().map(sb -> (sb.length()>1 ? sb.length() : "") + sb.substring(0,1)) .collect(Collectors.joining())); 

Another option:

 String text = "aaabccccCCaB"; StringBuilder result = new StringBuilder(); IntStream.range(0, text.length()+1) .filter(value -> value==0 || value == text.length() || text.charAt(value) != text.charAt(value-1)) //или так .reduce((l, r) -> { result.append(rl > 1 ? rl : "") .append(text.substring(l, l+1)); return r;}) //или сразу выводить на консоль без StringBuilder //.reduce((l, r) -> { // System.out.print(rl > 1 ? rl : ""); // System.out.print(text.substring(l, l+1)); // return r;}) .isPresent(); System.out.println(result.toString());. 
  • 2
    This is probably one of the least complicated implementation options on streams, but you must admit that most of the code here is taken by the "non-streaming" one. - Regent
  • It agree, here the most part of the code - implementation of functional interfaces. Delivered the largest to a separate variable - Alexander Potashev
  • Thank you, of all the options, the most sensible and simple IMHO is through .filter and .reduce and immediately to the console :) - Victor Kondratyuk
 String text = "aaabccccCCaB"; //разбиваем на группы String groups = Arrays.stream(text.split("")) .reduce("", (l, r) -> { int length = l.length(); if (length > 0 && l.toCharArray()[length - 1] == r.toCharArray()[0]) { return l + r; } else { return l + "@" + r; } }); //"схлапываем" группы String result = Arrays.stream(groups.split("@"))//.parallel() .filter(group -> !group.isEmpty()) .map(group -> (group.length() > 1 ? group.length(): "") + group.substring(0, 1)) .collect(Collectors.joining()); System.out.println(result); //3ab4c2CaB 

A shorter version of the same logic:

 String result = Arrays.stream(text.split(""))//.parallel() .reduce((l, r) -> l + (l.charAt(l.length() - 1) == r.charAt(0) ? "" : "@") + r) .map(groups -> groups.split("@")) .map(Stream::of).orElseGet(Stream::empty)//.parallel() .map(group -> (group.length() > 1 ? group.length() : "") + group.substring(0, 1)) .collect(Collectors.joining()); System.out.println(result); //3ab4c2CaB 

The second option is well parallelized (indicated //.parallel() ) where it can be done. The first option can be parallelized only after splitting into groups - in the 2nd stream.

    You can do something like this, but given that the characters must be processed one after the other - this is not a parallel matter:

     String str = "aaabccccCCaB"; List<Tuple2<Character, Long>> results = str.chars().mapToObj(c -> (char) c) .collect( // create collection to store elements LinkedList::new, // process each element (list, c) -> { Tuple2<Character, Long> lastCharacter = list.peekLast(); if (lastCharacter == null || lastCharacter.f0 != c) { list.add(new Tuple2<>(c, 1L)); } else { lastCharacter.f1 += 1; } }, // merge lists LinkedList::addAll ); String result = results.stream() .map(tuple -> (tuple.f1 > 1 ? "" + tuple.f1 : "") + tuple.f0) .collect(Collectors.joining()); System.out.println(result); // 3ab4c2CaB