Search code examples
javajava-8

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?


Solution

  • 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();
            if(firstNonRepeat.isPresent()) {
                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.orElse(null));
        }
    }
    

    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.

    The returned value is an Optional and should be treated safely. As pointed out, you can use isPresent() to check if a value has been found (see first print statement) or use orElse(...) to return a default value instead of throwing an exception (see print statement number 2 where I return null as the default to prevent the Optional to throw an Exception in case no repeated letter were found)