java 8 make a stream of the multiples of two - java

I'm practicing streams in java 8 and im trying to make a Stream<Integer> containing the multiples of 2. There are several tasks in one main class so I won't link the whole block but what i got so far is this:
Integer twoToTheZeroth = 1;
UnaryOperator<Integer> doubler = (Integer x) -> 2 * x;
Stream<Integer> result = ?;
My question here probably isn't related strongly to the streams, more like the syntax, that how should I use the doubler to get the result?
Thanks in advance!

You can use Stream.iterate.
Stream<Integer> result = Stream.iterate(twoToTheZeroth, doubler);
or using the lambda directly
Stream.iterate(1, x -> 2*x);
The first argument is the "seed" (ie first element of the stream), the operator gets applied consecutively with every element access.
EDIT:
As Vinay points out, this will result in the stream being filled with 0s eventually (this is due to int overflow). To prevent that, maybe use BigInteger:
Stream.iterate(BigInteger.ONE,
x -> x.multiply(BigInteger.valueOf(2)))
.forEach(System.out::println);

Arrays.asList(1,2,3,4,5).stream().map(x -> x * x).forEach(x -> System.out.println(x));
so you can use the doubler in the map caller

Related

Is there a possibility to make a new Stream out of 2 Streams in Java?

I am quite new to Java 8 Streams and have a question with faced me, doing homework for University.
If I have 2 Intstreams with have the same amount of Integers as
IntStream one = Arrays.stream(test.matrix).flatMapToInt(Arrays::stream);
IntStream two = Arrays.stream(test2.matrix).flatMapToInt(Arrays :: stream);
IntStream difference = one - two ???
is it possible to generate a new Stream holding the elementwhise difference of the two streams?
I have tried a lot like using stream.iterator(); which worked, but I am not allowed to use "normal" iterators, and should solve it with lambda expressions.
Has anyone a tip on how to solve this?
The operation combining two streams/lists is often called zip.
Unfortunately this operation is not provided in the Java 8 standard library.
You can read more about how users defined their own zip functions here: Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip)
Overall, you'll essentially just be iterating the arrays by index. If you only iterate by elements then you cannot combine the streams in a way analogous to a zip operation. First, define what subtracting matrices looks like. For now, I'll only define same size matrices:
public static void subtract(int[] origin, int[] operand) {
if (origin.length != operand.length) throw new IllegalArgumentException("Array lengths must match");
//Make a new result, so as not to muck with the original array
return IntStream.range(0, origin.length).map(i -> origin[i] - operand[i]).toArray();
}
Then generating the difference is a matter of passing in those matrices directly, and the stream is used simply as a matter of iterating both sets of elements. Thus the task could also be accomplished with a simple vanilla for loop. Nonetheless:
int[] result = subtract(one, two);
We can abstract the logic above as well to allow a more flexible set of operations:
public static int[] operate(int[] origin, int[] operand, IntBiFunction operation) {
if (origin.length != operand.length) throw new IllegalArgumentException("Array lengths must match");
return IntStream.range(0, origin.length)
.forEach(i -> result[i] = operation.apply(origin[i], operand[i]))
.toArray();
}
public static void subtract(int[] origin, int[] operand) {
//Producing result from each index matched element
return operate(origin, operand, (one, two) -> one - two);
}
This is where I'd see more of an advantage of using anything function api related.
If you wish to utilize a return of IntStream instead of int[], you can, but you'll run into the original issue of having to reference the arrays by index. You could really push it and supply an arbitrary amount of operations (IntBiFunction... operations), but I think you risk overcomplicating your logic.

2D Array stream reduce in java

I'm new to using streams in java and I have a question on using streams.
I have a double[][] in which i want to perform the summation of elements, for it I've written the following approach similar to C#Linq, but it doesn't seem to work.
Arrays.stream(myArray).reduce(0,(acc, i) -> acc + Arrays.stream(i).sum());
The error is that acc seems to be a double[], so it can't perform double[]+double.
In C#Linq the accumulator is assumed to be the same type as the seed(0 in this case). What am I missing here?
Thanks in advance.
If you look at the signature of reduce, the type of the identity has to be the type of the stream's elements. Which would be double[] in this case. That would also give acc the type of double[].
There is an overload where you can supply an accumulator of a different type, but you also need to pass a combiner, to combine 2 accumulators.
You can do this:
double result = Arrays.stream(myArray)
.reduce(0D, (acc, i) -> acc + Arrays.stream(i).sum(), Double::sum);
Where 0D is a double literal, and Double::sum is used to combine 2 accumulators.
Alternatively, it might be more convenient to do this:
double result = Arrays.stream(myArray)
.flatMapToDouble(DoubleStream::of)
.sum();

When should I use IntStream.range in Java?

I would like to know when I can use IntStream.range effectively. I have three reasons why I am not sure how useful IntStream.range is.
(Please think of start and end as integers.)
If I want an array, [start, start+1, ..., end-2, end-1], the code below is much faster.
int[] arr = new int[end - start];
int index = 0;
for(int i = start; i < end; i++)
arr[index++] = i;
This is probably because toArray() in IntStream.range(start, end).toArray() is very slow.
I use MersenneTwister to shuffle arrays. (I downloaded MersenneTwister class online.) I do not think there is a way to shuffle IntStream using MersenneTwister.
I do not think just getting int numbers from start to end-1 is useful. I can use for(int i = start; i < end; i++), which seems easier and not slow.
Could you tell me when I should choose IntStream.range?
There are several uses for IntStream.range.
One is to use the int values themselves:
IntStream.range(start, end).filter(i -> isPrime(i))....
Another is to do something N times:
IntStream.range(0, N).forEach(this::doSomething);
Your case (1) is to create an array filled with a range:
int[] arr = IntStream.range(start, end).toArray();
You say this is "very slow" but, like other respondents, I suspect your benchmark methodology. For small arrays there is indeed more overhead with stream setup, but this should be so small as to be unnoticeable. For large arrays the overhead should be negligible, as filling a large array is dominated by memory bandwidth.
Sometimes you need to fill an existing array. You can do that this way:
int[] arr = new int[end - start];
IntStream.range(0, end - start).forEach(i -> arr[i] = i + start);
There's a utility method Arrays.setAll that can do this even more concisely:
int[] arr = new int[end - start];
Arrays.setAll(arr, i -> i + start);
There is also Arrays.parallelSetAll which can fill an existing array in parallel. Internally, it simply uses an IntStream and calls parallel() on it. This should provide a speedup for large array on a multicore system.
I've found that a fair number of my answers on Stack Overflow involve using IntStream.range. You can search for them using these search criteria in the search box:
user:1441122 IntStream.range
One application of IntStream.range I find particularly useful is to operate on elements of an array, where the array indexes as well as the array's values participate in the computation. There's a whole class of problems like this.
For example, suppose you want to find the locations of increasing runs of numbers within an array. The result is an array of indexes into the first array, where each index points to the start of a run.
To compute this, observe that a run starts at a location where the value is less than the previous value. (A run also starts at location 0). Thus:
int[] arr = { 1, 3, 5, 7, 9, 2, 4, 6, 3, 5, 0 };
int[] runs = IntStream.range(0, arr.length)
.filter(i -> i == 0 || arr[i-1] > arr[i])
.toArray();
System.out.println(Arrays.toString(runs));
[0, 5, 8, 10]
Of course, you could do this with a for-loop, but I find that using IntStream is preferable in many cases. For example, it's easy to store an unknown number of results into an array using toArray(), whereas with a for-loop you have to handle copying and resizing, which distracts from the core logic of the loop.
Finally, it's much easier to run IntStream.range computations in parallel.
Here's an example:
public class Test {
public static void main(String[] args) {
System.out.println(sum(LongStream.of(40,2))); // call A
System.out.println(sum(LongStream.range(1,100_000_000))); //call B
}
public static long sum(LongStream in) {
return in.sum();
}
}
So, let's look at what sum() does: it counts the sum of an arbitrary stream of numbers. We call it in two different ways: once with an explicit list of numbers, and once with a range.
If you only had call A, you might be tempted to put the two numbers into an array and pass it to sum() but that's clearly not an option with call B (you'd run out of memory). Likewise you could just pass the start and end for call B, but then you couldn't support the case of call A.
So to sum it up, ranges are useful here because:
We need to pass them around between methods
The target method doesn't just work on ranges but any stream of numbers
But it only operates on individual numbers of the stream, reading them sequentially. (This is why shuffling with streams is a terrible idea in general.)
There is also the readability argument: code using streams can be much more concise than loops, and thus more readable, but I wanted to show an example where a solution relying on IntStreans is functionally superior too.
I used LongStream to emphasise the point, but the same goes for IntStream
And yes, for simple summing this may look like a bit of an overkill, but consider for example reservoir sampling
IntStream.range returns a range of integers as a stream so you can do stream processing over it.
like taking square of each element
IntStream.range(1, 10).map(i -> i * i);
Here are few differences that comes to my head between IntStream.range and traditional for loops :
IntStream are lazily evaluated, the pipeline is traversed when calling a terminal operation. For loops evaluate at each iteration.
IntStream will provides you some functions that are commonly applied to a range of ints such as sum and avg.
IntStream will allow you to code multiple operation over a range of int in a functional way which read more fluently - specially if you have a lot of operations.
So basically use IntStream when one or more of these differences are useful to you.
But please bear in mind that shuffling a Stream sound quite strange as a Stream is not a data structure and therefore it does not really make sense to shuffle it (in case you were planning on building a special IntSupplier). Shuffle the result instead.
As for the performance, while there may be a few overhead, you will still iterate N times in both case and should not really care more.
Basically, if you want Stream operations, you can use the range() method. For example, to use concurrency or want to use map() or reduce(). Then you are better off with IntStream.
For example:
IntStream.range(1, 5).parallel().forEach(i -> heavyOperation());
Or:
IntStream.range(1, 5).reduce(1, (x, y) -> x * y)
// > 24
You can achieve the second example also with a for-loop, but you need intermediate variables etc.
Also, if you want the first match for example, you can use findFirst() and cousins to stop consuming the rest of the Stream
It totally depends on the use case. However, the syntax and stream API adds lot of easy one liners which can definitely replace the conventional loops.
IntStream is really helpful and syntactic sugar in some cases,
IntStream.range(1, 101).sum();
IntStream.range(1, 101).average();
IntStream.range(1, 101).filter(i -> i % 2 == 0).count();
//... and so on
Whatever you can do with IntStream you can do with conventional loops. As one liner is more precise to understand and maintain.
Still for negative loops we can not use IntStream#range, it only works in positive increment. So following is not possible,
for(int i = 100; i > 1; i--) {
// Negative loop
}
Case 1 : Yes conventional loop is much faster in this case as toArray has a bit overhead.
Case 2 : I don't know anything about it, my apologies.
Case 3 : IntStream is not slow at all, IntStream.range and conventional loop are almost same in terms of performance.
See :
Java 8 nested loops with streams & performance
You could implement your Mersenne Twister as an Iterator and stream from that.

Working with a stream of streams

I want to work out if I can do something like this. Suppose I have a stream of the numbers 1 - 20. I want to utilise a feature such as a drop 3 (limit or skip I guess in Java terms?) and produce a stream of streams that is the numbers:
1 - 20, 4 - 20, 7- 20, etc
then possibly flat map these alll into one stream. I've tried various combinations of using Stream.iterate primarily to generate streams from streams, but I keep geting an IllegalStateException saying the stream has already operated upon or closed.
For example one may expect this code:
Stream.iterate(Stream.of(1,2,3,4,5), x -> x.skip(1).collect(Collectors.toList()).stream()).limit(5).flatMap(x -> x).forEach(x -> System.out.println(x));
To produce: 1,2,3,4,5,2,3,4,5,3,4,5,4,5,5
But it doesn't, it throws an exception. Am I missing something obvious here. I know of a takeWhile operation but don't think it's out until Java 9.
EDIT: I have managed to get an ugly solution to what I was trying to achieve using the following:
List<Integer> list = IntStream.rangeClosed(1, 20).boxed().collect(Collectors.toList());
Stream.iterate(list, x -> x.stream().skip(3).collect(Collectors.toList())).limit(7).map(x -> x.stream().limit(3).collect(Collectors.toList())).flatMap(List::stream).forEach(System.out::println);
So I have a list 1..20, then end up with a stream of lists 1..20, 4..20, 7..20, etc. If I then take the first 3 elements of each of these I end up with 1..3, 4..6, 7..9, etc. If I flatMap this, I get back to 1..20.
My question is, A. is there a way for me to not have to hardcode limit(7) above, so it automatically stops when I've processed all of the input element set, and B. I don't like having to collect into lists - can I not do this purely with streams prior to a collection at the end or even just printing after a flat map?
Perhaps you're looking for something like
IntStream.rangeClosed(1, 20)
.flatMap(i -> IntStream.rangeClosed(i, 20))
...which you can then do whatever you like with; e.g. .forEach(System.out::println).
If you want to do this for every third number, you're better off doing something like
IntStream.rangeClosed(0, 20/3)
.map(i -> 3 * i + 1)
.flatMap(i -> IntStream.rangeClosed(i, 20))

Combiner not executed in the Java Stream Reducer [duplicate]

I wrote this code to reduce a list of words to a long count of how many words start with an 'A'. I'm just writing it to learn Java 8, so I'd like to understand it a little better [Disclaimer: I realize this is probably not the best way to write this code; it's just for practice!].
Long countOfAWords = results.stream().reduce(
0L,
(a, b) -> b.charAt(0) == 'A' ? a + 1 : a,
Long::sum);
The middle parameter/lambda (called the accumulator) would seem to be capable of reducing the full list without the final 'Combiner' parameter. In fact, the Javadoc actually says:
The {#code accumulator} function acts as a fused mapper and
accumulator,
* which can sometimes be more efficient than separate mapping and reduction,
* such as when knowing the previously reduced value allows you to avoid
* some computation.
[Edit From Author] - The following statement is wrong, so don't let it confuse you; I'm just keeping it here so I don't ruin the original context of the answers.
Anyway, I can infer that the accumulator must just be outputting 1's and 0's which the combiner combines. I didn't find this particularly obvious from the documentation though.
My Question
Is there a way to see what the output would be before the combiner executes so I can see the list of 1's and 0's that the combiner combines? This would be helpful in debugging more complex situations which I'm sure I'll come across eventually.
The combiner does not reduce a list of 0's and 1's. When the stream is not run in parallel it's not used in this case so that the following loop is equivalent:
U result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;
When you run the stream in parallel, the task is spanned into multiple threads. So for example the data in the pipeline is partitioned into chunks that evaluate and produce a result independently. Then the combiner is used to merge this results.
So you won't see a list that is reduced, but rather 2 values either the identity value or with another value computed by a task that are summed. For example if you add a print statement in the combiner
(i1, i2) -> {System.out.println("Merging: "+i1+"-"+i2); return i1+i2;});
you could see something like this:
Merging: 0-0
Merging: 0-0
Merging: 1-0
Merging: 1-0
Merging: 1-1
This would be helpful in debugging more complex situations which I'm
sure I'll come across eventaully.
More generally if you want to see the data on the pipeline on the go you can use peek (or the debugger could also help). So applied to your example:
long countOfAWords = result.stream().map(s -> s.charAt(0) == 'A' ? 1 : 0).peek(System.out::print).mapToLong(l -> l).sum();
which can output:
100100
[Disclaimer: I realize this is probably not the best way to write this
code; it's just for practice!].
The idiomatic way to achieve your task would be to filter the stream and then simply use count:
long countOfAWords = result.stream().filter(s -> s.charAt(0) == 'A').count();
Hope it helps! :)
One way to see what's going on is to replace the method reference Long::sum by a lambda that includes a println.
List<String> results = Arrays.asList("A", "B", "A", "A", "C", "A", "A");
Long countOfAWords = results.stream().reduce(
0L,
(a, b) -> b.charAt(0) == 'A' ? a + 1 : a,
(a, b) -> {
System.out.println(a + " " + b);
return Long.sum(a, b);
});
In this case, we can see that the combiner is not actually used. This is because the stream is not parallel. All we are really doing is using the accumulator to successively combine each String with the current Long result; no two Long values are ever combined.
If you replace stream by parallelStream you can see that the combiner is used and look at the values it combines.

Categories

Resources