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):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
Not a big improvement so far... next I use the cool new Map.getOrDefault() to replace 7 lines of code with one:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
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:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |