I’ve coded a function that finds the unique characters in a string using Java 8 streams, based on an example here.
To me, it’s pretty non-intuitive, maybe because I’m still learning streams. Is there any way to make this more readable?
Here’s the code:
public static void main(String[] args) { String str = "aabcddeffg"; char[] charArray = str.toCharArray(); List<String> strList = new ArrayList<>(); for(int i=0; i< charArray.length; i++){ String myChar = String.valueOf(charArray[i]); strList.add(myChar); } Map<String, Long> myMap = strList.stream(). collect( Collectors.groupingBy( Function.identity(),Collectors.counting() ) ); myMap.forEach((k, v) -> { if (v == 1) { System.out.println(k); } }); } }
Advertisement
Answer
For calculating the actual frequencies, you’re pretty much at a minimal example already!
Map<Integer, Long> charFrequency = strList.stream() //Stream<String> .flatMapToInt(String::chars) //Stream<IntStream> -> IntStream .boxed() //IntStream -> Stream<Integer> .collect(Collectors.groupingBy( //Map<K, V> Function.identity(), //K == our integers Collectors.counting() //V == the number of them )); charFrequency.entrySet().stream() //Stream<Map.Entry<Integer, Long>> .filter(ent -> ent.getValue() == 1) //only entries with value of 1 .mapToInt(Map.Entry::getKey) //Stream<Entry> -> IntStream .forEach(c -> { System.out.println("Found unique character: " + ((char) c)); });
And for doing it for a single string, it’s even easier (you save the conversions):
Map<Integer, Long> charFrequency = someString.chars() //Stream<Integer> .collect(Collectors.groupingBy( //Map<K, V> Function.identity(), //K == our integers Collectors.counting() //V == the number of them ));
To that end, I would ensure that your code is simply consistent and readable. Use consistent indentation and comment how the stream steps per line, for example.
Edit: I’ve left the below (old) answer just to be informative for the OP, but it doesn’t answer the actual question.
Well, there’s always Stream#distinct.
Calcuating the “distinct” (not unique) characters:
List<Integer> distinctChars = strList.stream() //Stream<String> .flatMapToInt(String::chars) //Stream<IntStream> -> IntStream .distinct() //unique IntStream .boxed() //unique Stream<Integer> .collect(Collectors.toList()); //List<Integer> distinctChars.forEach(c -> { System.out.println("Found distinct char: " + ((char) (int) c)); });
If you want to avoid collecting it, you can also avoid all the hassle around the type boxing:
strList.stream() //Stream<String> .flatMapToInt(String::chars) //Stream<IntStream> -> IntStream .distinct() //unique IntStream .forEach(c -> { System.out.println("Found distinct char: " + ((char) c)); });