Java Examples - Episode 3

Robert Torok December 30, 2018
Java Examples - Episode 3

Continuing the series of solving simple Java tasks this week we'll take a look at three new exercises.

Counting Odd Numbers

This task might seem boring a little bit, but this is one of the assignments that can leverage one of the new features introduced in Java 8, namely the Stream API.

It's been a while since Java 8 came out, but I still see some developers struggling with the lambda expressions or Stream API. If you haven't done so, it's highly recommended that you get familiar with these features.

The following tasks scratches only the surface of the Stream API, but it's good enough to demonstrate that using the Stream API for the same task usually results in better code.

Let's look at the task then!

Write a function that accepts a list of numbers as a String (separated by whitespace) and returns a `List` containing only the odd numbers from the original list.

Example:

countOddNumbersOriginal("0 1 2 3 4 5 6 7 8 9 10")

That should return:

0 2 4 6 8

The old way

Nothing interesting here, first we split up the input parameter, then convert the numbers one by one to int and add them to the list if they can be divided by two without remainder.

public List<Integer> countOddNumbersOriginal(String line) {
    List<Integer> oddNumbers = new LinkedList<>();
    for (String s : line.split(" ")) {
        int number = Integer.valueOf(s);
        if (number % 2 == 0) {
            oddNumbers.add(number);
        }
    }
    return oddNumbers;
}

Using Stream API

And this how the Stream API can be used to solve this task. Look at the two solutions! The one above exactly specifies how to collect the odd numbers. On the other hand, this solution rather tells what to do. It is way more expressive than the previous one.

public List<Integer> countOddNumbers(String line) {
    return Arrays.stream(line.split(" "))
            .map(Integer::valueOf)
            .filter(n -> n % 2 == 0)
            .collect(Collectors.toList());
}

Parsing Command Line Arguments

The following one shows how to implement a very simple command line parser. Obviosuly, the implementation below is not something that can be used in production, but it's a good task to practice.

Write a method that parses command line arguments! The first parameter of the method should be the raw command line arguments. The second parameter specifies which argument's value the parser should return. Each argument name begins with `--`.

Example:

getValue("--color blue --level max", "color")

Should return: blue

A possible solution could like like below:

public String getValue(String arguments, String key) {
    if (arguments == null) {
        throw new IllegalArgumentException("arguments cannot be null!");
    }
    
    String parameterName = "";
    String parameterValue = "";
    Map<String, String> options = new HashMap<>();
    for (String s : arguments.split(" ")) {
        if (s.startsWith("--")) {
            parameterName = s.replaceAll("\\-\\-", "");
        } else {
            parameterValue = s;
            options.put(parameterName, parameterValue);
        }
    }

    return options.get(key);
}

There's a big caveat though. In this particular task it does not matter (next time I'll bring some tasks where it does), but be very careful with the String class' replace and replaceAll methods.

They're often mixed up and eventually resulting in hard to spot bugs.

What's the difference? In a nutshell:

  • replace accepts char or CharSequence
  • replaceAll accepts regex!

Consideraing that, replace(".", "") and replaceAll(".", "") will do different things (check it!) and you might be surprised by seeing the results of replaceAll if you are not familiar that it works with regex.

Compressing Words

Again, a task for checking basic algorithm skills. In addition to the algorithm you use, it also reveals how you concatenate Strings. For some reason, this question (concatenating Strings) frequently comes up in interviews.

Provide a function that accepts repeating characters and compresses them! Compressing means that after printing the individual character we should also print how many times it was repeated.

Example:

compressWord("eefww");

Should return:

e2f1w2

A simple solution to the task:

public String compressWord(String word) {
    char prevChar = word.charAt(0);
    int prevPosition = 0;
    StringBuilder output = new StringBuilder();
    for (int i = 1; i < word.length(); i++) {
        char currentChar = word.charAt(i);
        if (currentChar != prevChar) {
            output.append(prevChar).append(i - prevPosition);
            prevPosition = i;
        }
        prevChar = currentChar;
    }
    output.append(prevChar).append(word.length() - prevPosition);

    return output.toString();
}