Looks like a major performance bottleneck for n-gram searches was
appending words to the result string, which could easily reach several
hundred thousand characters in length. So, instead of using '+' to
concatenate strings, it now uses a StringBuilder. This improves
performance substantially - a search for "tion" used to take ~1000ms,
now it takes ~100ms, and searching for a single character such as 't'
could take over 45000ms, now it takes around 2000ms. While there is
still a perceptible delay for the most common n-grams, performance is
now much better. 

Also, the old function that searched for n-grams read from the file
every time it searched and appended every word in the file to an
ArrayList, even if it was already in the list (meaning that the list
could reach several million strings after a few searches). This
ArrayList was only used in a different search function that for some
reason was only called when searching for empty strings/whitespace. So,
there is now one function that reads the strings and makes the
ArrayList, and another function that scans through the ArrayList for
words.