2D Array stream reduce in java - 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();

Related

Stream flatMap vs map

To filter a List we can use
a1.stream().filter(x->x>6).forEach(System.out::println);
and to filter two lists I used FlatMap
Stream<List<Integer>> s2=Stream.of(a1,a2);
s2.flatMap(x->x.stream()).sorted().filter(x->x>6).forEach(System.out::println);
but I tried filtering in this way for a single List
Stream<List<Integer>> s1=Stream.of(a1);
s1.map(x->x.stream()).filter(x->x>2).forEach(System.out::print);
I got an error--- The operator > is undefined for the argument type(s) Stream<Integer>, int
but when I use the flatMap in the map no error why
Stream<List<Integer>> s1=Stream.of(a1);
s1.flatMap(x->x.stream()).filter(x->x>2).forEach(System.out::print);
If you are calling map(x->x.stream()) on a Stream<List<Integer>>, you'll get a Stream<Stream<Integer>>. You cannot apply .filter(x->x>2) on the elements of that Stream, since those elements are Stream<Integer>s, and the > operator requires two numeric operands.
If you use flatMap instead of map, you are converting your Stream<List<Integer>> to a Stream<Integer> whose elements are all the elements of all the Lists of the original Stream. Therefore, you can apply the .filter(x->x>2) filter on them.
Stream<List<Integer>> s1 = Stream.of(a1);
s1.map(x -> x.stream()).filter(x -> x > 2).forEach(System.out::print);
This will give error as you are not using flat map so filtering not possible whole (Streams >2). You can try like this:
Stream<List<Integer>> s1 = Stream.of(a1);
s1.map(x -> x.stream()).filter(x -> x.findFirst().get() > 2).forEach(System.out::print);
As we cannot apply.filter(x->x>2) on we will requires two numeric operands. s1.map(x -> x.stream()).filter(x -> x.findFirst().get() > 2).forEach(System.out::print); if we will take this then filter will find find matching element value which is greater than 2. In short we have to use flatmap to find greater than 2 in those streams or any operation to fetch numeric operands from stream then only it work.

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.

java 8 make a stream of the multiples of two

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

turn string separated by spaces to arraylist of integers in one line

Say I have the String "5 12 4". I'd like to turn that into an ArrayList of Integers containing 5,12 and 4 in a single functional line.
I feel there should be a way to do this by combining split(" "), turning it into a stream, using mapToInt(s->Integers.parseInt(s)) and collect(Collectors.toList()). Something like:
ArrayList<Integer> nextLine = Arrays.stream(inputLine.split(" "))
.mapToInt(s->Integer.parseInt(s))
.collect(Collectors.toList());
But that does not work, because mapToInt give me ints instead of Integers.
I know how to do it using a loop. I would like a way to do it in a single stream operation if it exists.
You can use Integer#valueOf. Note you should use Stream#map and not Steam#mapToInt though:
List<Integer> nextLine =
Arrays.stream(inputLine.split(" "))
.map(Integer::valueOf)
.collect(Collectors.toList());
mapToInt returns an IntStream and you cannot accumulate primitive elements into a ArrayList<T> therefore you can utilise the map operation which would yield a Stream<Integer> and then you can accumulate the elements into a ArrayList<T>.
That said, even if you change .mapToInt(s -> Integer.parseInt(s)) to .map(s -> Integer.parseInt(s)) your code still will not compile as the receiver type for the result is of type ArrayList<Integer> whereas the collect terminal operation will return a List<Integer> in this specific case.
Therefore to solve the remaining issue you can either have the receiver type as List<Integer> or leave the receiver type as is and then do .collect(Collectors.toCollection(ArrayList::new)); for the reduction operation yielding a specific List implementation.
Another variant to the already posted answer would be:
ArrayList<Integer> resultSet =
Pattern.compile(" ")
.splitAsStream(inputLine)
.map(Integer::valueOf)
.collect(Collectors.toCollection(ArrayList::new));

create map of int occurrences using Java 8

I am aware my question is very similar to Count int occurrences with Java8
, but I still cannot solve my case, which must be easier to solve.
The need is to count how many times integers repeat in a stream of integers (will be coming from a file, may be up to 1000000 integers). I thought it might be useful to create a map, where Integer will be a Key, and number of occurrences will be a value.
The exception is
Error:(61, 66) java: method collect in interface
java.util.stream.IntStream cannot be applied to given types;
required:
java.util.function.Supplier,java.util.function.ObjIntConsumer,java.util.function.BiConsumer
found: java.util.stream.Collector> reason: cannot
infer type-variable(s) R
(actual and formal argument lists differ in length)
However, in Java 8 there is a Collectors.groupingBy, which should suffice
Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream)
The problem is that my code is not compiling and I do not see - why.
I simplified it to this:
Map<Integer,Integer> result = IntStream.range(0,100).collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
What is the reason for not compiling?
Thank you in advance :)
IntStream has one method collect where the second argument operates on an int not an Object. Using boxed() turns an IntStream into a Stream<Integer>
Also counting() returns a long.
Map<Integer, Long> result = IntStream.range(0, 100).boxed()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
I have solved the task at hand using Peter's idea.
I'm posting the solution in case somebody is studying Java 8 and does not want to repeat my mistakes.
The task was to:
read numbers from file
find how often each number occurs
find how
many pairs can be found for numbers occurring more than once. For
example, if number 3 occurs 4 times, we will have 6 pairs (I used Apache's
CombinatoricsUtils.binomialCoefficient for that).
My solution:
long result = Arrays.stream(Files.lines(Paths.get(fileName)).mapToInt(Integer::parseInt).collect(() ->
new int[BYTE_MAX_VALUE], (array, value) -> array[value] += 1, (a1, a2) ->
Arrays.setAll(a1, i -> a1[i] + a2[i]))).map((int i) -> combinatorics(i, 2)).sum()
If you're open to using a third party library with primitive collections, you can potentially avoid the boxing operations. For example, if you use Eclipse Collections, you can write the following.
IntBag integers = Interval.oneTo(100).collectInt(i -> i % 10).toBag();
Assert.assertEquals(10, integers.occurrencesOf(0));
Assert.assertEquals(10, integers.occurrencesOf(1));
Assert.assertEquals(10, integers.occurrencesOf(9));
An IntHashBag is implemented by using an IntIntHashMap, so neither the keys (your integers) nor the values (the counts) are boxed.
The same can be accomplished if you loop through your file and add your results to an IntHashBag from an IntStream.
MutableIntBag integers = IntBags.mutable.empty();
IntStream.range(1, 101).map(i -> i % 10).forEach(integers::add);
Assert.assertEquals(10, integers.occurrencesOf(0));
Assert.assertEquals(10, integers.occurrencesOf(1));
Assert.assertEquals(10, integers.occurrencesOf(9));
Note: I am a committer for Eclipse Collections.

Categories

Resources