Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Improvements to anagram

Tags:

java

anagram

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);
       }
    }
}
like image 977
Bill Avatar asked Jan 25 '26 00:01

Bill


1 Answers

Bug fix: lettersOnly's return value is lost

Your 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);

    ...
}

Dead code removal

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]);             
}

Refactoring the if statements

While 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);
}

Advanced

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.

Loop reversal

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);
    }
}
like image 106
John Kugelman Avatar answered Jan 26 '26 16:01

John Kugelman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!