Sunday, October 11, 2015

Java 8

Wow, have I really not posted anything since January?

I'm coming up to speed on Java 8.  I've been working in Groovy for like a year and a half so these concepts are not new, but the syntax is just different enough that I have to think about it.

Here is some code I wrote a while back.  It is a solution to a Cracking the Coding Interview problem to write a boolean method taking two strings as input and returning whether one string is a permutation of the other (same characters in a different order):

public static boolean isPermutation(String s1, String s2) {
return signature(s1).equals(signature(s2));
}
public static Map<Character, Integer> signature(String s) {
Map<Character, Integer> result = new HashMap<Character, Integer>();
for (int i = 0; i < s.length(); i++) {
Character c = s.charAt(i);
Integer count = result.get(c);
if ( count == null) {
result.put(c, 1);
} else {
result.put(c, count+1);
}
}
return result;
}
view raw Java81.java hosted with ❤ by GitHub

This is basically Java 6 the way we've been writing it for the last 10 years or so.

isPermutation stays the same so I'll just be showing changes to signature.

Use a Java 8 lambda and a stream to iterate through the String.  Note the use of String.chars() returning an IntStream which is where the stream comes from.  You'd think there's be a CharStream, but no, so I'm using a rather awkward conversion to get the int back to a char:

public static Map<Character, Integer> signature(String s) {
Map<Character, Integer> result = new HashMap<Character, Integer>();
s.chars().forEach((int i) -> {
char c = Character.toChars(i)[0];
Integer count = result.get(c);
if ( count == null) {
result.put(c, 1);
} else {
result.put(c, count+1);
}
});
return result;
}
view raw java82.java hosted with ❤ by GitHub

Not a big improvement so far... next I use the cool new Map.getOrDefault() to replace 7 lines of code with one:

public static Map<Character, Integer> signature(String s) {
Map<Character, Integer> result = new HashMap<Character, Integer>();
s.chars().forEach(i -> {
char c = Character.toChars(i)[0];
result.put(c, result.getOrDefault(c, 0) + 1);
});
return result;
}
view raw java83.java hosted with ❤ by GitHub


That is starting to be some more expressive and maintainable code... finally there's nothing sacred about the signature being implemented as a Map<Character,Integer>, so let's change that to Map<Integer,Integer> which will make it possible to remove the awkward int to char conversion and make the lambda body an expression rather than a block:



public static Map<Integer, Integer> signature(String s) {
Map<Integer, Integer> result = new HashMap<Integer, Integer>();
s.chars().forEach(i -> result.put(i, result.getOrDefault(i, 0) + 1));
return result;
}
view raw java84.java hosted with ❤ by GitHub