A problem with looping while selecting longest and shortest words using a regex

Viewed 43

I need to select all words of the maximum length and all of the minimum length.

For example:

When I was younger, so much younger than today 
I never needed anybody's help in any way 
But now these days are gone, I'm not so self-assured 
Now I find I've changed my mind 
I've opened up the doors

Example of the Output:

Min: I, s, m
Max: younger, anybody, assured, changed

I already figured out the algorithm, but it simply terminates after the sorting loop (with an int k), and I can't why, because the debugger says nothing because of my unit testing. It simply stops. Could you help me to figure out why it doesn't work pls? P.S. I can't use container classes

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Part2 {

    public static void main(String[] args) {
        String input = Util.getInput("part2.txt");
        System.out.print(convert(input));
    }

    public static String convert(String input) {
        Pattern p = Pattern.compile("[A-zА-я]+");
        Matcher m = p.matcher(input);
        int i = 0;
        String[] arr = new String[100];
        while (m.find()){
            arr[i] = m.group(0);
            i++;
        }
        StringBuilder longestResult = new StringBuilder();
        longestResult.append("Max: ");
        StringBuilder shortestResult = new StringBuilder();
        shortestResult.append("Min: ");
        int longest = 0;
        int shortest = arr[0].length();
        for (int k = 0; k < arr.length; k++){
            if (arr[k].length() > longest){
                longest = arr[k].length();
            }
            if (arr[k].length() < shortest){
                shortest = arr[k].length();
            }
        }
        for ( String word : arr) {
            if (word.length() == longest) {
                longestResult.append(word + ", ");
            }
            if (word.length() == shortest) {
                shortestResult.append(word + ", ");
            }
        }
        return shortestResult.substring(1, shortestResult.length() - 2) +
                longestResult.substring(1, longestResult.length() - 2);
    }
}
1 Answers

The problem you are facing is quite simple and easy to debug even without a debugger. Your code has a main-method and can be run by the IDE of your choosing to debug this. I suggest you have a look on how to debug your code to help you with more complex problems later on.


Just by looking at the code I see a conceptual problem. You are creating a String array which gets partially filled in the while-loop. All these elements are all null unless you are assigning a value to them. So the first i elements will have a String set, all other elements are null. Afterwards, you are looping over the whole array (k < arr.length). The problem arises inside this for-loop because you try to call length() on the null-elements. arr[k].length() will throw a NullPointerException starting from the i-th element. The same problem happens later again with word.length(). Running your code confirms the above:

Exception in thread "main" java.lang.NullPointerException

at the line with if (arr[k].length() > longest){. One way to fix this (out of many) is to only iterate over the elements of the array that actually contain a value (0 .. (i-1)) like this: for (int k = 0; k < (i - 1); k++){. The same applies to the second for-loop as well.


A better solution is to use String#split() which accepts a regex defining where to split the input and returns you an array directly. This array has no null-elements and contains just enough elements to hold the result. You can replace your while-loop with this and have a solution that works for more than 100 words without having a hard-coded limit. If you use this, you don't need to modify your loops.

String[] arr = input.split("[\\p{Punct}\\s]+");

Additional remarks:

  • Use a StringJoiner instead of a StringBuilder to have a clean way to get the comma separated values: StringJoiner longestWords = new StringJoiner(", ") then use longestWords.add(word) and finally constuct the result "Max: " + longestWords.
  • You need to find a way to not have duplicate values in the result.
Related