Just finished up a recent homework, but I know it could be more efficient. It reads two words from command line, ignores spaces and punctuation, and determines if they are anagrams. What I have is below; it's fully functional as far as I can tell.
/**
* Find out if a string is an anagram of another string
*
*/
import java.util.Arrays;
public class Anagram
{
public static void main(String[] args)
{
if (args.length != 2)
System.out.println("You did not enter two words!");
else
printWords(args[0], args[1]);
}
// method to determine whether two strings have the same chars
public static boolean areAnagrams(String wordOne, String wordTwo)
{
// new strings for letters only
String ltrsOnlyOne = lettersOnly(wordOne);
String ltrsOnlyTwo = lettersOnly(wordTwo);
// convert strings to lowercase char arrays
char[] first = ltrsOnlyOne.toLowerCase().toCharArray();
char[] second = ltrsOnlyTwo.toLowerCase().toCharArray();
// sort char arrays using sort method
Arrays.sort(first);
Arrays.sort(second);
if (Arrays.equals(first, second))
return true;
else
return false;
}
public static String lettersOnly(String word)
{
int length = word.length();
StringBuilder end = new StringBuilder(length);
char x;
for (int i = (length - 1); i >= 0; i--) {
x = word.charAt(i);
if (Character.isLetter(x)) {
end.append(x);
}
}
return end.toString();
}
public static void printWords(String wordOne, String wordTwo)
{
boolean b = areAnagrams(wordOne, wordTwo);
if (b == true) {
System.out.println(wordOne + " is an anagram of "+ wordTwo);
}
if (b == false) {
System.out.println(wordOne + " is not an anagram of "+ wordTwo);
}
}
}
lettersOnly's return value is lostYour lettersOnly method does not modify its arguments in place, it returns the new strings. When you call it you need to do something with this return value. Otherwise you are simply calling it and throwing away the result.
// method to determine whether two strings have the same chars
public static boolean sameChars(String wordOne, String wordTwo)
{
lettersOnly(wordOne);
lettersOnly(wordTwo);
wordOne = lettersOnly(wordOne);
wordTwo = lettersOnly(wordTwo);
...
}
In main() you call sameChars without checking its return value. Since sameChars doesn't modify its arguments in place or have any other side effects, this call is dead code and should be removed.
public static void main(String[] args)
{
if (args.length != 2) {
System.out.println("You did not enter two words!");
}
sameChars(args[0], args[1]);
printWords(args[0], args[1]);
}
if statementsWhile your if statements are correct, using == to compare against true and false is not idiomatic. There is no need for explicit comparisons when you are using boolean values. It is shorter and reads better to simply omit the comparisons.
if (sameChars(wordOne, wordTwo) == true) {
if (sameChars(wordOne, wordTwo)) {
System.out.println(wordOne + " is an anagram of "+ wordTwo);
}
if (sameChars(wordOne, wordTwo) == false) {
if (!sameChars(wordOne, wordTwo)) {
System.out.println(wordOne + " is not an anagram of "+ wordTwo);
}
Notice how == false is replaced by the equivalent ! (NOT).
Actually, there is no reason to repeat the call to sameChars. Having code that looks so similar ought to raise a small alarm in your mind. "There should be a way to eliminate the duplicate code," you think. Ah yes, let's switch the second if to an else!
if (sameChars(wordOne, wordTwo)) {
System.out.println(wordOne + " is an anagram of "+ wordTwo);
}
else {
System.out.println(wordOne + " is not an anagram of "+ wordTwo);
}
Along similar lines, you could simplify the following set of statements:
if (Arrays.equals(first, second))
return true;
else
return false;
When the result of equals() is true you return true. Otherwise when it is false you return false. If you think about it, you are effectively just returning whatever equals returned. You could actually eliminate the if and else entirely and simply return the result of equals directly. Huh!
return Arrays.equals(first, second);
This change is a bit more advanced than the earlier one. You might find this change a bit odd if you've never seen it before. But it's indeed equivalent to the four line version.
Typically when programmers write for loops they start at 0 and iterate up to some upper bound. Is there a reason you wrote your loop as a descending loop?
for (int i = (length - 1); i >= 0; i--) {
x = word.charAt(i);
if (Character.isLetter(x)) {
end.append(x);
}
}
Some programmers will loop backwards like this in a questionable attempt to be "more efficient". As in, they only perform the calculation (length - 1) once, so it's slightly faster. Personally I consider this a waste of time and brain power.
In fact, written backwards your lettersOnly function has the odd side effect of returning the strings in reverse order after it strips out the unwanted characters. It turns out this doesn't matter since you later sort the characters into alphabetical order. But this is a type of thing that could bite you later. It just so happened that you got away with the reversal because of the sorting. I would switch the loop to iterate in normal 0-to-n order:
for (int i = 0; i < length; ++i) {
x = word.charAt(i);
if (Character.isLetter(x)) {
end.append(x);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With