I spend 2 hours trying to convert one example from Scala lang to Java lang, the example are simple in scala but prove to be more trick in Java than I expected. The main concepts are lambdas, passing functions as parameters, and parallel collections. My question is: How to improve the Java code, there are a few things I would like to do:
- first return the result from the function in the measure function, I try use generics with no immediate success (I guess I do something wrong)
- A better way to make a list from a sequence, I do not expect something so easy as (0 to 10) but maybe..
- A better permutation implementation, some java library. The algorithm in scala is performing 10 times faster, but my is just one copy past from stack overflow as I can't find one natively or library.
- Why I need to return null in the lambda expression even from a void expression? In scala I know that everything returns something even if it is Nothing
This is not a comparison between the two languages, well in practice this could be, this is a learning code, from the perspective of a guy who learn coding in Java, then starts coding in Scala for fun and functional learning them back to Java 8 to see what is new. That way, I'm not a expert in Scala and in functional programming.
Let's go to the code
Scala Version:
object ParalelTest {
def measure[T] (func: => T):T ={
val start = System.nanoTime()
val result = func
val elapsed = System.nanoTime() - start
println("A execução do metodo demorou %s ns".format(elapsed))
result
}
var sum:Int = _
def heavyComputation ="abcdefghij".permutations.foreach(i=> sum+=1)
def main(args: Array[String]) {
measure(heavyComputation)
println(s"Soma das permutacoes: ${sum}")
measure((0 to 10).foreach(i => heavyComputation))
measure((0 to 10).par.foreach(i => heavyComputation))
println(s"Soma das permutacoes: ${sum}")
}
}
Java Version (the best I could in the time)
public class ParallelTest implements Runnable {
int sum =0;
private void measure(Callable callable) {
long start = System.nanoTime();
try {
callable.call();
} catch (Exception e) {
e.printStackTrace();
}
long elapsed = System.nanoTime() - start;
System.out.println(String.format("A execução demorou %s ns",elapsed));
}
private Integer heavyComputation(){
sum = 0;
permutation("abcdefghij").stream().forEach(i -> sum++);
return sum;
}
public Collection<String> permutation(String str) {
List<String> permutations = new ArrayList<>();
permutation("", str, permutations);
return permutations;
}
private void permutation(String prefix, String str, Collection<String> permutations) {
int n = str.length();
if (n == 0) permutations.add(prefix);
else {
for (int i = 0; i < n; i++)
permutation(prefix + str.charAt(i), str.substring(0, i) + str.substring(i+1, n), permutations);
}
}
@Override
public void run() {
System.out.println("Running one heavycomputations");
measure(this::heavyComputation);
// First we need to fill the list (there is no '0 to 10')
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 9; i++) {
list.add(i);
}
// Sequencial
System.out.println("Running 10 sequencial heavy computations");
measure(()-> {
list.stream().forEach(i -> this.heavyComputation());
return null;
});
// Parallel
System.out.println("Running 10 in parallel");
measure(() -> {
list.parallelStream().forEach(i -> this.heavyComputation());
return null;
});
}
}
IntStream.rangeClosed(1, 10)
does exactly that. Or you can userange()
:IntStream.range(0, 10)
. Other than that, Luciano's answer is great. Unfortunately, Java stdlib is not as comprehensive as Scala one, so we have to rely on third-party libraries for some tasks (e.g. generating permutations). – Vladimir Matveev