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));
});