Broken keyboard challenge Inspired by Reddit r/dailyprogrammer


  Help! My keyboard is broken, only a few keys work any more. If I tell you what keys work, can you tell me what words I can write?  


The program works fine, not really any additions I wish to make to it. Was just wanting some general feedback on the layout, structure and efficiency. 

Note: The dictionary used can be found in the given link.

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;

/**
 * Created on 7/1/2016.
 *
 * Inspired by r/dailyprogrammer
 * https://www.reddit.com/r/dailyprogrammer/comments/3pcb3i/20151019_challenge_237_easy_broken_keyboard/
 */


public class BrokenKeyboard {

    private static List&lt;String&gt; wordDict;
    private static final String dictPath = "Dictionaries/EnglishWords.txt";

    /**
     * The main acquires input from the user then
     * applies it to the other below methods.
     * @param args Unused.
     */

    public static void main(String args[]) {
        BrokenKeyboard.setDict(dictPath); // Stores dictionary in list.
        System.out.println("Processing...");
        System.out.println(String.join(", ", findMatches("bikn", wordDict)));
    }

    /**
     * Used to load a given text file into memory.
     * @param filePath Used to specify the path of the file that needs to be loaded into memory.
     */
    private static void setDict(String filePath){
        try {
            wordDict = Files.readAllLines(new File(filePath).toPath());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Used to compare a given key to the text file loaded into memory.
     * @param key The inputted set of characters compared to the given dictionary.
     * @param dict The list of words that will be used to compare to the key.
     * @return The list of matches made when comparing the key with the given dictionary.
     */
    private static List&lt;String&gt; findMatches(String key, List&lt;String&gt; dict){
        List&lt;String&gt; matchList = new ArrayList&lt;&gt;();
        for(String word : dict){
            String temp = word; // Stores word so that it can be manipulated.
            for(char character : key.toCharArray()) {
                temp = temp.replace(Character.toString(character), "");
            }
            if(temp.equals("")){
                matchList.add(word);
            }
        }
        return matchList;
    }
}
setDict() is a bit of a misnomer; loadDict() would be more accurate.  Since the loaded dictionary becomes part of the global state, it would be more appropriate to make it an instance variable than a static variable.  If loading fails, you shouldn't just print a stack trace and attempt to continue execution — that defeats the purpose of exceptions.  In general, if you don't have a good way to handle an exception, just propagate it (by declaring throws IOException).

Your technique for checking whether a word consists solely of a limited set of characters is to do repeated string replacement.  That would cause a lot of temporary objects to be allocated and discarded.  A more appropriate tool to use would be a regular expression match (using Pattern.compile("^[bikn]*$"), for example).

Since this is a filtering exercise, it would be more eloquently written using Stream&lt;String&gt;.filter(Predicate) instead of List&lt;String&gt;.

Although String args[] is syntactically valid, String[] args is considered more idiomatic in Java, since String[] is considered the type.

Suggested solution

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class BrokenKeyboard {
    public static Predicate&lt;String&gt; typableUsing(String availableKeys) {
        // CAVEAT: availableKeys is assumed not to contain regex metacharacters
        // such as ^ [ ] - \
        return Pattern.compile("^[" + availableKeys + "]*$").asPredicate();
    }

    public static Stream&lt;String&gt; dictionary(File f) throws IOException {
        return Files.lines(f.toPath());
    }

    public static void main(String[] args) throws IOException {
        System.out.println(
            BrokenKeyboard.dictionary(new File(args[1]))
                          .filter(BrokenKeyboard.typableUsing(args[0]))
                          .collect(Collectors.joining(", "))
        );
    }
}


It is possible to resolve the caveat using Pattern.quote().  I'll leave it as an exercise to the reader.  =)