Naresh Bandaru Naresh Bandaru - 2 months ago 23
Java Question

How To Find First Repeated And Non-Repeated Character In A String Using java8

I have a working example to find the first repeated and
Non-repeated character in a String Using java 7

Below is the working example

public class FindFirstRepeatedAndNonRepeatedChar {
static void firstRepeatedNonRepeatedChar(String inputString) {

HashMap<Character, Integer> charCountMap = new HashMap<Character, Integer>();

char[] strArray = inputString.toCharArray();

for (char c : strArray) {
if (charCountMap.containsKey(c)) {
charCountMap.put(c, charCountMap.get(c) + 1);
} else {
charCountMap.put(c, 1);
}
}

for (char c : strArray) {
if (charCountMap.get(c) == 1) {
System.out.println("First Non-Repeated Character In '" + inputString + "' is '" + c + "'");

break;
}
}

for (char c : strArray) {
if (charCountMap.get(c) > 1) {
System.out.println("First Repeated Character In '" + inputString + "' is '" + c + "'");

break;
}
}
}

public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("Enter the string :");
String input = sc.next();
firstRepeatedNonRepeatedChar(input);
}
}


Can anyone help me on how to refactor the above code using Java8?

Answer

With some helpful input, I adapted my answer with less code:

public class FirstRepeat {

    public static void main(String[] args) {
        Map<Character, Long> collect =  "abcsdnvs".chars().mapToObj(i -> (char)i).collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()));
        collect.forEach( (x,y) -> System.out.println( "Key: " + x + " Val: " + y));

        Optional<Character> firstNonRepeat = collect.entrySet().stream().filter( (e) -> e.getValue() == 1).map(e -> e.getKey()).findFirst();
        System.out.println("First non repeating:" + firstNonRepeat.get());
        Optional<Character> firstRepeat = collect.entrySet().stream().filter( (e) -> e.getValue() > 1).map(e -> e.getKey()).findFirst();
        System.out.println("First repeating:" + firstRepeat.get());
    }
}

What the above does:

  1. Create a stream of Characters: "abcsdnvs".chars().mapToObj(i -> (char)i)
  2. Group these characters using a collector: .collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()));

The grouping is split in 3 different parts:

  1. Classifier

As pointed out, I can use Function.identity() for that. The equivalent of x -> x. This means we are grouping by the actual character.

  1. mapFactory

We are using LinkedHashMap::new for this. The reason is that we need to preserve the insertion order in order to find the first element. The default implementation uses a HashMap which will not preserve insertion order.

  1. Downstream collector

Since we are using a grouping, we need to decide how to collect the grouped elements. In this case, we need the count of occurrences. For this, you can use: Collectors.counting() which will simply sum up how many elements are available of a given character.

The program then prints:

Key: a Val: 1
Key: b Val: 1
Key: c Val: 1
Key: s Val: 2
Key: d Val: 1
Key: n Val: 1
Key: v Val: 1
First non repeating:a
First repeating:s

We are using a stream operation to find the first element (based on the filter):

Optional<Character> firstNonRepeat = collect.entrySet().stream().filter( (e) -> e.getValue() == 1).map(e -> e.getKey()).findFirst();

Where we can stream the grouped elements, filter them by a value of ( >1 for first repeate, ==1 for first non-repeating character). The findFirst method then returns the Element, if such an element is present.

Comments