Seniority is in details

Seniority is in details

My brother in law has started learning programming. He is doing that in Java. He is doing exercises and asked me for a feedback.

One of the exercises he did is to find anagrams that aren’t case sensitive (it doesn’t matter if you have LARGE or small letters). So “Xaver” “verAx” should evaluate as true.

His solution:

public class Anagram {

	public static void main(String[] args) {

		String test = "";
		String original = "";

		System.out.println(isAnagram("HOUBA", "uhAoBx"));

	}

	public static boolean isAnagram(String test, String original) {
		List<Character> testList = new ArrayList<>();
		List<Character> originalList = new ArrayList<>();

		for(int i=0; i < test.length(); i++) {
			testList.add(test.toLowerCase().charAt(i);
		}

		for(int i=0; i < original.length(); i++) {
			originalList.add(original.toLowerCase().charAt(i);
		}

		Collections.sort(testList);
		Collections.sort(originalList);

		return testList.equals(originalList) && testList.size() == originalList.size() ? true : false;

	}

}

I am no Java developer, but the code can be simplified and optimised.

import java.util.Arrays;

public class AnagramChecker {

    public static boolean areAnagrams(String str1, String str2) {
        if (str1.length() != str2.length()) {
            return false;
        }

        char[] arr1 = str1.toLowerCase().toCharArray();
        char[] arr2 = str2.toLowerCase().toCharArray();

        Arrays.sort(arr1);
        Arrays.sort(arr2);

        return Arrays.equals(arr1, arr2);
    }

    public static void main(String[] args) {
        System.out.println(areAnagrams("listen", "silent"));  // Expected output: true
        System.out.println(areAnagrams("hello", "world"));    // Expected output: false
    }
}

He didn’t know about toCharArray() method. That’s alright, now he knows about it.

It shows there are two missing concepts in his understanding.

Check this block:

for(int i=0; i < test.length(); i++) {
	testList.add(test.toLowerCase().charAt(i);
}

It calls toLowerCase() on the whole string on every character which is suboptimal.

test = test.toLowerCase();
for(int i=0; i < test.length(); i++) {
	testList.add(test.charAt(i);
}

This is significantly better solution because you calculate toLowerCase just once.

The second block is:

return testList.equals(originalList) && testList.size() == originalList.size() ? true : false;

All that ? true : false part is not needed. Expression returns boolean anyway so there can be just:

return testList.equals(originalList) && testList.size() == originalList.size();

Also, checking strings length equality in the end of the method is not optimal. These should be checked at the beginning as it is fast way how to eliminate many cases in fast way that don’t need to convert and sort any strings.

Let me just quickly share a solution in Clojure so you can compare the difference:

(require '[clojure.string :as str])

(defn are-anagrams? [s1 s2]
  (let [s1 (str/lower-case s1)
        s2 (str/lower-case s2)]
    (and (= (count s1) (count s2))
         (= (frequencies s1) (frequencies s2)))))

;; Test the function
(println (are-anagrams? "Listen" "Silent")) ;; Expected output: true
(println (are-anagrams? "Hello" "World")) ;; Expected output: false

As you can see, being a developer means knowing a lot about how the code is evaluated and what functions are available. This could help to deliver code that is more readable, shorter and faster at the same time.