Java 8 Stream

Find frequency of characters in a string using Java 8 Stream

Find frequency of characters in a string using Java 8 Stream

This is one of the commonly asked questions in a coding interview. The solutions which I could find on most of the popular sites were not utilizing the Java 8 features. so here is a solution for this problem using Streams and Collectors.

the input would be

String input = "bookkeeper";

1. Not considering the order of characters.

Step 1: Split the input string to get each letters using input.split("")

Step 1: To ignore the case sensitivity we’ll use input.toLowerCase().split("")

Step 3: using the terminal operator collect and Collectors.groupingBy(e -> e, Collectors.counting()) we’ll reduce the stream to a Map with key as String and frequency as Long.

Step 4: Get the maps entrySet as stream .entrySet().stream()

Step 5: Map the entry to the desired output format and print each using a forEach method.

Stream.of(input.toLowerCase().split(""))
        .collect(Collectors.groupingBy(e -> e, Collectors.counting()))
        .entrySet()
        .stream()
        .map(e -> e.getKey()+e.getValue()+" ")
        .forEach(System.out::print);

Output: p1 r1 b1 e3 k2 o2

2. Character frequency in the order of their occurrence.

HashMap doesn’t preserve the order in which key is added. To preserve the order we need to use LinkedHashMap.

Stream.of(input.toLowerCase().split(""))
        .collect(Collectors.groupingBy(e -> e, LinkedHashMap::new, Collectors.counting()))
        .entrySet()
        .stream()
        .map(e -> e.getKey()+e.getValue()+" ")
        .forEach(System.out::print);

Output: b1 o2 k2 e3 p1 r1

3. Print characters frequency in alphabetic order.

Sorting the stream based on the key of map Map.Entry.comparingByKey().

Note that this could be done by using just sort() after .map operation as the map operation would generate string.

Stream.of(input.toLowerCase().split(""))
        .collect(Collectors.groupingBy(e -> e, Collectors.counting()))
        .entrySet()
        .stream()
        .sorted(Map.Entry.comparingByKey())
        .map(e -> e.getKey()+e.getValue()+" ")
        .forEach(System.out::print);

Output: b1 e3 k2 o2 p1 r1

4. Print characters frequency in the order of most frequent one to least frequent one.

For printing the output based on the frequency of characters, sort the map using value Map.Entry.comparingByValue().

this would display the output in ascending order. since we want the most frequent once in the beginning we’ll pass comparator (a, b) -> b.compareTo(a)

Stream.of(input.toLowerCase().split(""))
        .collect(Collectors.groupingBy(e -> e, Collectors.counting()))
        .entrySet()
        .stream()
        .sorted(Map.Entry.comparingByValue((a, b) -> b.compareTo(a))))
        .map(e -> e.getKey()+e.getValue()+" ")
        .forEach(System.out::print);

Output: e3 k2 o2 p1 r1 b1

Taking a look at return type at each step.

Code Snippet

Conclusion

The complete code with all the mentioned methods.

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FrequencyCount {


    public static void main(String[] args) {
        String input = "bookkeeper";

        var mapOfChar = getMapOfCharacterFrequency(input);
        var linkedHashMapOfChar = getLinkedHashMapOfCharacterFrequency(input);

        System.out.println("\nfreqOfCharacters");
        freqOfCharacters(mapOfChar.entrySet());

        System.out.println("\nfreqInOrderOfAppearance");
        freqOfCharacters(linkedHashMapOfChar.entrySet());

        System.out.println("\nfreqInOrderOfAlphabetic");
        freqInOrderOfAlphabetic(mapOfChar.entrySet());

        System.out.println("\ncharactersInOrderOfMostOccurring");
        charactersInOrderOfMostOccurring(mapOfChar.entrySet());

    }

    private static Map<String, Long> getMapOfCharacterFrequency(String input) {
        return Stream.of(input.split(""))
                .collect(Collectors.groupingBy(e -> e, Collectors.counting()));
    }

    private static Map<String, Long> getLinkedHashMapOfCharacterFrequency(String input) {
        return Stream.of(input.split(""))
                .collect(Collectors.groupingBy(e -> e, LinkedHashMap::new, Collectors.counting()));
    }

    private static void freqOfCharacters(Set<Map.Entry<String, Long>> input) {
        input.stream()
                .map(e -> e.getKey()+e.getValue()+" ")
                .forEach(System.out::print);
    }

    private static void freqInOrderOfAlphabetic(Set<Map.Entry<String, Long>> input) {
        input.stream()
                .sorted(Map.Entry.comparingByKey())
                .map(e -> e.getKey()+e.getValue()+" ")
                .forEach(System.out::print);
    }

    private static void charactersInOrderOfMostOccurring(Set<Map.Entry<String, Long>> input) {
        input.stream()
                .sorted(Map.Entry.comparingByValue((o1, o2) -> o2.compareTo(o1)))
                .map(e -> e.getKey()+e.getValue()+" ")
                .forEach(System.out::print);
    }

}

Bonus section

The same code can be used for finding the frequency for words in a sentence.

Just add a space (or word separator) in the split regex input.toLowerCase().split(" ");


Continue Reading

comments powered by Disqus