Dziś oficjalnie rozpoczynamy serię Java By Example. Jestem przekonany że najlepszą nauką programowania jest po prostu praktyka. W serii Java By Example postawimy sobie jakiś problem który będziemy rozwiązywać, kody źródłowe będą dostępne oczywiście na GitHubie. Seria JavaByExample jest częścią kursu programowania w Javie.

01. Zadanie

Dzisiejsze zadanie jest zadaniem które pamiętam z podstaw programowanie:

Napisz program który wypisze liczbę występowania poszczególnych znaków, występujących we wprowadzonym przez użytkownika zdaniu.

Przykład 1:
Użytkownik wprowadza zdanie: ala
Odpowiedź programu: a -2 , l – 1

Przykład 2:
Użytkownik wprowadza zdanie: Ala ma kota
Odpowiedź programu: A – 1, a – 3, t – 1, k – 1, l – 1, m – 1, o – 1

Uwaga: Zwróć uwagę że wielka litera A jest liczona osobno od małej literki a

02. Uwaga!

Przede wszystkim – postaraj się rozwiązać to zadanie samodzielnie. Poniżej znajdziesz moje rozwiązanie, opis wraz z całym kodem źródłowym!

Przed rozpoczęciem mojego rozwiązania chciałbym przypomnieć o jednej bardzo ważnej sprawie. Pamiętaj o tym że nie ma jednego konkretnego sposobu na napisanie programu który rozwiązuje dany problem. Jak to powtarzał jeden z moich profesorów, konkretny program możemy napisać na milion sposobów i każdy z nich może być poprawny!

STOP -Poniżej znajdziesz moje rozwiązanie tego zadania – postaraj się rozwiązać najpierw samemu! W razie problemów możesz przeczytać moje rozwiązanie oraz zajrzeć na GitHuba!

03. Moje rozwiązanie

Ok – Czas pomyśleć nad rozwiązaniem. Zadanie możemy podzielić na 3 oddzielne elementy:

1) Pobranie od użytkownika wartości
2) Policzenie ile razy w zdaniu występuje konkretny znak
3) Wypisanie wyniku na ekranie

Podzielenie programu na osobne odpowiedzialności jest bardzo dobrą praktyką – każda część programu jest odpowiedzialna za tylko jedną funkcjonalność  – „Single responsibility principle”.

Ok! Zabieramy się za kodowanie. Pierwsza część programu jest prosta:

System.out.println("Input sentence: ");
Scanner scanner = new Scanner(System.in);
String userInput = scanner.nextLine();

Pobranie zdania od użytkownika mamy za sobą 🙂

Kolejna część jest tak naprawdę sercem naszego programu, musimy policzyć występowania poszczególnych znaków. Pobrane przez nas zdanie użytkownika jest przypisane do zmiennej typu String, żaden nasza metoda przyjmie String jako parametr. Metoda zwróci nam ma Mapę – jako klucz będzie znakiem, wartość liczbę która będzie reprezentować ile razy dany znak występował we wprowadzonym zdaniu.

public static Map<Character, Integer> countCharacters(String userInput) {
}

Cała metoda wygląda następująco:

public static Map<Character, Integer> countCharacters(String userInput) {
    if(userInput == null || userInput.isEmpty()) return Collections.emptyMap();

    Map<Character, Integer> characterCounter = new HashMap<>();
    for (char c : userInput.toCharArray()) {
        Integer value = characterCounter.get(c);
        if (value != null) {
            value++;
        } else {
            value = 1;
        }
        characterCounter.put(c,value);
    }

    return characterCounter;
}

Opis:
W pierwszym kroku sprawdzam co przyszło do metody, jeśli dostaliśmy nulla, lub pustego Stringa, nie ma sensu nic robić. Po prostu zwracamy pustą mapę. Jeśli jednak coś dostaliśmy od użytkownika do przetworzenia, mamy trochę roboty 😊

Tworzymy pustą mapę, oraz pętlę po wszystkich znakach z wprowadzonego zdania. W pętli staramy się pobrać z mapy wartość dla konkretnego znaku.

Jeśli dostaniemy wartość różną od null -> zwiększamy ją – znak wystąpił w zdani kolejny raz,  jeśli dostaniemy null, znaczy to że konkretnego znaku nie ma jeszcze w mapie – wartość ustawiamy na 1. W obu przypadkach dodajemy parę znak – ilość do mapy, w przypadku nowej wartości będziemy mieć kolejny element w mapie, w przypadku istniejącego już takiego klucza, zastąpimy go z nowa wartością.

Uwaga! Aktualizacja rozwiązania

Powyższa metoda w oparciu o pętlę, choć poprawna, to można ją zdecydowanie uprościć używając Streamów (Bardzo dziękuję czytelnikowi za zwrócenie uwagi!), streamy na blogu nie były jeszcze poruszane, ale warto zapoznać się również z tym rozwiązaniem:

public static Map<Character, Long> countCharactersStream(String userInput) {
  return userInput.chars().mapToObj(c -> (char) c)
      .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}

Metoda jest teraz niezwykle prosta! Cały wprowone zdanie zamieniamy w Stream, oraz zbieramy wszystkie unikalne elementy, oraz zliczamy ich występowanie używając

Collectors.groupingBy(Function.identity(), Collectors.counting())

Pozostaje nam jeszcze ostatnia część zadania – Wypisanie wyniku:

public static void printCountedInput(Map<Character, ? extends Number> characterCounter) {
   for (Map.Entry<Character, ? extends Number> entry : characterCounter.entrySet()) {
      System.out.println("Sign: "+entry.getKey().toString()+" Count: "+entry.getValue());
   }
}

Cały kod:

System.out.println("Input sentence: ");
Scanner scanner = new Scanner(System.in);
String userInput = scanner.nextLine();
printCountedInput(countCharacters(userInput));

Podsumowanie

Pamiętaj o punkcie drugim w którym wspominałem że nie ma jednego dobrego rozwiązania 😊 to jest tylko moja propozycja! Cały kod do zadania jest dostępny na Githubie! Możesz ściągnąć cały kod i uruchomić go u siebie 😊