0

I started learning Java 8 features, streams and lambdas in particular, and I'm quite confused. I tried to rewrite the piece of code below

MyClass graph = new MyClass(V); for (int i = 0; i < numOfVertices; i++) { for (int j = 0; j < numOfVertices; j++) { if (adjMatrix[i][j] != 0) { graph.addEdge(i, j, adjMatrix[i][j]); } } } 

I wrote this:

int b = IntStream.range(0, numOfVertices - 1).parallel() .map(i -> (IntStream.range(0, numOfVertices - 1)) .map(j -> { if (adjMatrix[i][j] != 0) { graph.addEdge(i, j, adjMatrix[i][j]); } })); 

Of course it doesn't work. What would be the proper way to rewrite the code above? I'm also looking for the best performance, so I attempted to use the parallel method.

Thanks!

6
  • 1
    "Of course it doesn't work" - give more details. Is there an error, or is the result incorrect, or something else? Commented Nov 7, 2019 at 16:06
  • 2
    best would be something you would have to measure; in 99% of the cases stick to the plain old loops you have. Commented Nov 7, 2019 at 16:07
  • 3
    Note that parallelising this only makes sense if your graph object can allow parallel updates without using a lock. I suspect this is not the case unless your graph data structure is quite carefully designed with this requirement in mind. Commented Nov 7, 2019 at 16:08
  • 1
    First, why did you change numOfVertices to numOfVertices - 1 when turning to stream? Second, is graph.addEdge(…) thread safe? Did you test without .parallel()? Commented Nov 7, 2019 at 16:09
  • It's my mistake, it should be just numOfVertices. Commented Nov 7, 2019 at 16:54

2 Answers 2

1

Regardless to performance this is a simple form rewriting it with stream:

 IntStream.range(0, numOfVertices - 1) .forEach(i -> IntStream.range(0, numOfVertices - 1) .filter(j -> adjMatrix[i][j] != 0) .forEach(j -> graph.addEdge(i, j, adjMatrix[i][j]))); 
Sign up to request clarification or add additional context in comments.

2 Comments

Yep, I agree, I've completely ignored the inside loops logic
Thanks for the answer! It works as intended, however, as you mentioned, the performance is not that great
0

Short answer

If you want "best performance", the shorter time in iteration: Use "for loop" for small matrix, for bigger cases you can consider use "parallel streams"

.. long answer

I created a test to measure the average time of execution one of three options.

"For loop:"

for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { doSomething(numOfVertices[i][j]); } } 

"IntStream.range:"

IntStream.range(0, size) .forEach(i -> (IntStream.range(0, size)) .forEach(j -> { doSomething(numOfVertices[i][j]); })); 

"IntStream.range parallel:"

IntStream.range(0, size).parallel() .forEach(i -> (IntStream.range(0, size)) .forEach(j -> { doSomething(numOfVertices[i][j]); })); 

This section was removed as I could not simpulate it's time cost:

 if (adjMatrix[i][j] != 0) { graph.addEdge(i, j, adjMatrix[i][j]); } 

Below is the whole test code that perform measurement for different matrix. It runs every case 10000 times and gives the average time. Here are the results for run on my computer. For small matrix (form 1 x 1 to 100 x 100) for loop is the best option. When matrix is big (more than 500 x 500) using "IntStream.range parallel" is faster. "IntStream.range" is always worse than "For loop" for every case.

Size: 1 IntStream.range parallel: 0.002300 IntStream.range: 0.002200 For loop: 0.000000 Size: 5 IntStream.range parallel: 0.023000 IntStream.range: 0.000500 For loop: 0.000000 Size: 10 IntStream.range parallel: 0.025800 IntStream.range: 0.000300 For loop: 0.000100 Size: 50 IntStream.range parallel: 0.042200 IntStream.range: 0.004300 For loop: 0.000900 Size: 100 IntStream.range parallel: 0.035000 IntStream.range: 0.006000 For loop: 0.004300 Size: 500 IntStream.range parallel: 0.060200 IntStream.range: 0.102000 For loop: 0.079600 Size: 1000 IntStream.range parallel: 0.122000 IntStream.range: 0.379800 For loop: 0.347400 
 public static void main(String[] args) { Arrays.asList(1, 5, 10, 50, 100, 500, 1000).forEach( size -> { System.out.println("Size: " + size); Integer[][] numOfVertices = createArray(size); Map<String, Runnable> toRun = Map.of( "For loop:", () -> { for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { doSomething(numOfVertices[i][j]); } } }, "IntStream.range:", () -> { IntStream.range(0, size) .forEach(i -> (IntStream.range(0, size)) .forEach(j -> { doSomething(numOfVertices[i][j]); })); }, "IntStream.range parallel:", () -> { IntStream.range(0, size).parallel() .forEach(i -> (IntStream.range(0, size)) .forEach(j -> { doSomething(numOfVertices[i][j]); })); } ); int howManyTimes = 10000; Map<String, Double> map = new HashMap<>(); toRun.entrySet().forEach(e -> { double time = (((double)IntStream.range(0, howManyTimes).mapToObj( (x) -> executionTime(e.getValue())) .reduce(Long::sum).get()) / howManyTimes); map.put(e.getKey(), time); }); map.entrySet().forEach(e -> { System.out.println(String.format("%25s %f", e.getKey(), e.getValue())); }); } ); } private static Integer[][] createArray(int size) { Integer[][] val = new Integer[size][size]; IntStream.range(0, size).forEach( i -> IntStream.range(0,size).forEach( j -> val[i][j] = (int)(Math.random() * 100) ) ); return val; } private static int doSomething(int val) { return val * val; } private static long executionTime(Runnable execThis) { long startTime = System.currentTimeMillis(); execThis.run(); long endTime = System.currentTimeMillis(); return (endTime-startTime); } 

1 Comment

Sorry for the late response, but thanks for such a great explanation with examples!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.