LogMessage FRQ Solution

LogMessage, the second free response question on the 2016 AP Computer Science exam, has you working with strings and ArrayLists.

Part A

For the constructor you are given a String parameter message that is guaranteed to contain exactly one : character. Everything to the left of the colon goes into an instance variable called machineId and everything after goes into description.

Using the AP Java subset, this is an indexOf problem. First you need to find where the colon is in message and then use that number to substring out everything before and after.

public LogMessage( String message ) {
    int pos = message.indexOf(":");
    machineId = message.substring(0, pos);
    description = message.substring(pos + 1);
}

pos is the position of the colon.

The other option, if you’re willing to go a bit out of the Java subset, is to use the String split method. It splits a string into an array of strings based on a regular expression. Since a colon isn’t part of regex, we can just put the colon in quotes.

public LogMessage( String message ) {
  String[] spl = message.split(":");
  machineId = spl[0];
  description = spl[1];
}

Part B

Next you’re going to implement a method called containsWord that returns true or false if a keyword is found in description matching a specific set of rules.

  • keyword is found in description
  • keyword is either
    • at the start of description and has a space after it
    • at the end of description and has a space before it
    • somewhere else in description with a space before and after

For a solution that stays within the subset, we’re going to use indexOf and substring.

public boolean containsWord( String keyword ) { 
    if (keyword.equals(description))
        return true;
    else if (description.indexOf(keyword + " ") == 0) 
        return true; 
    else if (description.indexOf(" " + keyword + " ") >= 0) 
        return true; 
    else if (description.length() > keyword.length()
        && description.indexOf(" " + keyword) == description.length() - keyword.length() - 1) 
        return true; 
    return false; 
}

I broke this down to 4 cases, any of which should return true

  • description and keyword are exactly the same
  • description starts with keyword followed by a space
  • keyword is somewhere in description with spaces both before and after
  • keyword is at the end of description with a space before it

The last case took a bit more work because it failed when the keyword and description were the same length, but not equal. So my last condition checks both of those things.

Shorter solutions

This is a solution that one of my students shared with me.

public boolean containsWord( String keyword ) { 
    return (" " + description + " ").indexOf(" " + keyword + " ") >= 0;
}

Their code pads description with spaces on both ends and looks for keyword padded with spaces. This is one of those snippets of code that I really like to see from students because it shows they really understand the question and the methods needed to solve it.

This also could have been solved using regular expressions, although that would have been way out of the AP Java subset.

public boolean containsWord( String keyword ) { 
    return description.matches(".*(\\s|^)" + keyword + "(\\s|$).*");
}

The short explanation is that this looks to see if description matches anything, followed by either the start of the string or whitespace, followed by keyword followed by whitespace or the end of the string, followed by anything.

Even if I was really comfortable with regex I still wouldn’t have written this solution during the actual AP exam. I’d be too worried that the grader might not be familiar with regex and would count it incorrect.

Part C

And last you’re going through a list of LogMessage objects and removing those that contain a specific keyword.

public List<LogMessage> removeMessages( String keyword ) {
    List<LogMessage> out = new ArrayList<>();
    for (int i=messageList.size() - 1; i>=0; i--) {
        if (messageList.get(i).containsWord(keyword)) {
            out.add(0, messageList.remove(i)); 
        }
    }
    return out; 
}

I created a new List called out that’s going to contain the LogMessages that were removed.

I then went backwards through messageList looking for LogMessages that contained the keyword. I like to go backwards when removing from a List because it keeps the loop from skipping over elements after a removal.

And when it finds one to remove, it gets added at the beginning of out. It has to be inserted at position zero because of the backwards loop. Otherwise out would be backwards.

This site contains affiliate links. If you click an affiliate link and make a purchase we may get a small commission. It doesn't affect the price you pay, but it is something we must disclose.