Streams
August 25, 2014 6 Comments
I tried to use lambdas to swap elements in the char[] array. Does this mean that I am trying to change the stream while it is streaming ? This code is from http://www.cs.uofs.edu/~mccloske/courses/cmps144/invariants_lec.html but this question is unrelated to those concepts.
If that is a problem then a new stream will do. How should this be done ? I am not looking for a Comparator. I would like to work with this code as it is without using any API but lambdas.
I am printing using lambdas in this code now.
public class DutchNationalFlag { private static final int N = 10; private static char[] flags = new char[]{'R','B','B','R','R','B','B','R','R','B'}; public static void main( String... argv){ new String(flags).chars().mapToObj(i -> (char)i).forEach(System.out::println); int m = 0, k = 0; while (m != N) { if (flags[m] == 'B') { } else { swap(flags,k,m); k = k+1; } m = m+1; } new String(flags).chars().mapToObj(i -> (char)i).forEach(System.out::println); } private static void swap(char[] flags, int k, int m) { char temp = flags[k]; flags[k] = flags[m]; flags[m] = temp; } }
Possible Solution 1:
This doesn’t do exactly what the original code does. It doesn’t swap and doesn’t advance k which is the boundary between ‘B’ and ‘R’.But it produces the result.
Stream<Character> stream1 = IntStream.range(0, flags.length).mapToObj(i -> (char)flags[i]); Stream<Character> stream2 = IntStream.range(0, flags.length).mapToObj(i -> (char)flags[i]); Stream.concat(stream2.filter(x-> (x == 'B')), stream1.filter( y->(y == 'R') )).forEach(System.out::println);
new String(flags).chars().sorted().forEach(i -> System.out.println((char)i));
Yes. I was trying to do this by swapping without using a API call to learn how it is done using only streams. Your code works too.
It did highlight to me that from an IntStream I wouldn’t be able to sort it in descending order without using boxed() so that I could pass a Comparator. IntStream probably needs a new method “sortedReversed”.
I think that Streams are a bit higher level than swapping individual elements. Streams is more about what, rather than how. A lot of the API has been designed so you can throw parallel() into the chain and things will mostly still work.
The algorithm for swapping accesses elements in order and then backtracks (directly accesses) to swap old elements. A Stream is closer to an Iterator in that it doesn’t assume you can go backwards, but also it isn’t, in that it could choose to not do things in order at all (in the case of parallel it will usually use Fork/Join and approach the task in a divide and conquer way),
I thought ‘zip’ could help. So just as an academic exercise one could create two streams one lagging behind the other by one element. A new stream could contain the result. I tried this but my lambda knowledge wasn’t enough. FJ can still be used in this case.
Streams tries to be as lazy as possible: it won’t necessarily zip them all up and then apply the next operation on them afterwards. It could try to evaluate the entire chain in parallel and so it would want to chop up the zip operation as well and this doesn’t work as each part can’t be evaluated until the previous has been.
customers.stream.mapToInt(Customer::getSalary).sum();
will flow more like (in the serial case):
int sum = 0;
for (Customer c : customers) {
int salary = c.getSalary();
sum += salary
}
rather than:
List salaries = List();
for (Customer c : customers) { salaries.add(c.getSalary(); } // Operation 1
int sum = 0;
for(int salary : salaries) { sum += salary; } // Operation 2
If you are interested (my memory is a little fuzzy about it now), you might like :
http://www.infoq.com/presentations/java-streams-scala-parallel-collections