I have just discovered the new Java 8 stream capabilities. Coming from Python, I was wondering if there was now a neat way to do operations on arrays like summing, multiplying two arrays in a "one line pythonic" way ?
Thanks
There are new methods added to java.util.Arrays to convert an array into a Java 8 stream which can then be used for summing etc.
int sum = Arrays.stream(myIntArray).sum();
Multiplying two arrays is a little more difficult because I can't think of a way to get the value AND the index at the same time as a Stream operation. This means you probably have to stream over the indexes of the array.
//in this example a[] and b[] are same length
int[] a = ...
int[] b = ...
int[] result = new int[a.length];
IntStream.range(0, a.length).forEach(i -> result[i] = a[i] * b[i]);
Commenter #Holger points out you can use the map method instead of forEach like this:
int[] result = IntStream.range(0, a.length).map(i -> a[i] * b[i]).toArray();
You can turn an array into a stream by using Arrays.stream():
int[] ns = new int[] {1,2,3,4,5};
Arrays.stream(ns);
Once you've got your stream, you can use any of the methods described in the documentation, like sum() or whatever. You can map or filter like in Python by calling the relevant stream methods with a Lambda function:
Arrays.stream(ns).map(n -> n * 2);
Arrays.stream(ns).filter(n -> n % 4 == 0);
Once you're done modifying your stream, you then call toArray() to convert it back into an array to use elsewhere:
int[] ns = new int[] {1,2,3,4,5};
int[] ms = Arrays.stream(ns).map(n -> n * 2).filter(n -> n % 4 == 0).toArray();
Be careful if you have to deal with large numbers.
int[] arr = new int[]{Integer.MIN_VALUE, Integer.MIN_VALUE};
long sum = Arrays.stream(arr).sum(); // Wrong: sum == 0
The sum above is not 2 * Integer.MIN_VALUE.
You need to do this in this case.
long sum = Arrays.stream(arr).mapToLong(Long::valueOf).sum(); // Correct
Please note that Arrays.stream(arr) create a LongStream (or IntStream, ...) instead of Stream so the map function cannot be used to modify the type. This is why .mapToLong, mapToObject, ... functions are provided.
Take a look at why-cant-i-map-integers-to-strings-when-streaming-from-an-array
Related
I want to essentially map a set to an array after calling someMethod on each element. Should I loop through using a foreach loop and a "i" variable outside, or use a regular for loop and iterator?
int[] arr = new int[set.size()];
int i = 0;
for(int ele : set) {
arr[i] = someMethod(ele);
i++;
}
or
int[] arr = new int[set.size()];
Iterator<Integer> iterator = set.iterator();
for(int i=0; i<set.size(); i++) {
arr[i] = someMethod(iterator.next().intValue());
}
Both methods are equally good but for loop is slightly faster than forEach loop,
There are some other methods to convert set to array given below.
You can use toArray() function :
int[] arr = new int[set.size()];
arr = set.toArray(arr);
or you can use stream in java 8 or above:
int[] arr = set.stream().toArray(int[] ::new);
Another method is by using Arrays.copyOf
int[] arr = Arrays.copyOf(set.toArray(), set.size(), Integer[].class);
tl;dr
Both of your loops are equivalent.
You can use streams to make a one-liner, where the stream does the looping for you.
int[] arrayOfInts =
Set
.of( 1 , 2 , 3 )
.stream()
.mapToInt( Integer :: intValue )
.map( integer -> Math.multiplyExact( integer , integer ) )
.sorted()
.toArray();
arrayOfInts = [1, 4, 9]
Either loop is good
Both of your loops work well.
Choose whichever is easiest to read and understand. For me that would usually be the for-each syntax, as seen in your first loop.
By the way, that first loop could be shortened. The i++ syntax pulls out and utilizes the value of i before incrementing. So you can nest the i++ inside your array index accessor.
Set < Integer > set = Set.of( 1 , 2 , 3 );
int[] arr = new int[ set.size() ];
int i = 0;
for ( int element : set ) {
arr[ i++ ] = Math.multiplyExact( element , element ); // Auto-boxing converts `Integer` objects into `int` primitive values.
}
System.out.println( "arr = " + Arrays.toString( arr ) );
arr = [1, 9, 4]
Stream instead of loop
The Answer by Pranav Choudhary is correct. But you also mentioned wanting to apply a method to modify your number before assigning to the array.
Java streams make it easy to perform such a modification during collection.
Define our Set of Integer objects.
Set < Integer > set = Set.of( 1 , 2 , 3 );
Create a stream of int primitives, boxed from our Integer objects.
IntStream intStream = set.stream().mapToInt( Integer :: intValue );
Apply your modification. Here we square each number. The Math.multiplyExact method is handy because it will throw an ArithmeticException if we overflow the limits of the 32-bit int type. We collect each resulting square as an int in our array.
int[] arrayOfInts = intStream.map( integer -> Math.multiplyExact( integer , integer ) ).toArray();
Dump to console.
System.out.println( "arrayOfInts = " + Arrays.toString( arrayOfInts ) );
When run.
arrayOfInts = [9, 4, 1]
Notice the order of the output. A Set by definition has no determined iteration order. To emphasize that, the Set.of method reserves the right to use an implementation of Set that randomly changes its iteration. So each time you run this code you may see a different ordering of results.
If you care about order, add that to your stream work. Add a call to sorted().
int[] arrayOfInts = intStream.map( integer -> Math.multiplyExact( integer , integer ) ).sorted().toArray();
When run, we get consistent ordering. See this code run live at IdeOne.com.
arrayOfInts = [1, 4, 9]
Not that I recommend it, but we could turn this code into a one-liner.
int[] arrayOfInts = Set.of( 1 , 2 , 3 ).stream().mapToInt( Integer :: intValue ).map( integer -> Math.multiplyExact( integer , integer ) ).sorted().toArray();
Or:
int[] arrayOfInts =
Set
.of( 1 , 2 , 3 )
.stream()
.mapToInt( Integer :: intValue )
.map( integer -> Math.multiplyExact( integer , integer ) )
.sorted()
.toArray();
See this code run live at IdeOne.com.
arrayOfInts = [1, 4, 9]
Both versions are correct in the sense that they both give the same result.
However, in my opinion the first version is the preferable of the two.
It is easier to understand for an average Java programmer.
It is more concise. Fewer lines of code. Simpler statements.
It will probably be faster ... unless the optimizer is amazingly clever.
If it was me, I would save a line and write it like this:
for(int ele : set) {
arr[i++] = someMethod(ele);
}
but that is just my old C habits shining through.
As others have pointed out, in Java 8+ there is an even more concise way to write this; e.g. see this answer.
With java8:
Integer[] arr = set.stream().map(element -> doSomething(element)).toArray(Integer[]::new);
Where doSomething() is your method which you can call -
calling someMethod on each element.
This question already has answers here:
How do I fill arrays in Java?
(8 answers)
Closed 4 years ago.
I know how to get an int[] with a range of numbers:
int[] array = IntStream.of(0, 3).toArray();
But how can I get it with fixed length and one specific number?
IntStream.generate(() -> x).limit(y)
is what you need. Replace x and y with any number you like and you will produce a stream that has y lots of the number x.
You can obviously then call toArray or do whatever operation you want.
IntStream.generate creates an infinite stream using the supplier.
Here's one way:
int[] array = IntStream.rangeClosed(1, n).map(x -> m).toArray();
should produce an array of length n filled with m.
The following Q&A has other answers that use other approaches, such as the Arrays.fill method.
How do I fill arrays in Java?
Or simpler again.
// n elements of value m
int[]a=new int[n];
Arrays.fill(a,m);
Or even simpler with an API that was written for that:
int[] arr = new int[10];
Arrays.setAll(arr, x -> 1);
// or if you have enough data for parallel to make any difference
Arrays.parallelSetAll(arr, x -> 1);
I am trying to make a program to apply an operation between all values in an array or string, however the operation is a bitwise ^ operation between all the elements. Is there anything that can do this? Between my knowledge of lists and arrays I don't even know where to start.
EX:
int[] n = {0,1,2,3,4,6}
// program that can do the operation for 0^1^2^3^4^6
You can't do that with arrays but you can achieve what you wanna do with the list using streams;
List<Integer> l = Arrays.asList(1,2,3,4,6);
int res = l.stream().reduce(0, (m,k) -> m +k);
I want to iterate just the half of an array in java. Is there any elegant way to shorten this up, eg with a for-each loop?
int[] array = {0,1,2,3,4,5};
for (int i = 0; i<array.length/2; i++)
{
System.out.println(array[i]);
}
If you converted the array into a list using the asList method of the Arrays class in Java, then you can use the forEach method in the List class in Java to print out each element of the list in one single line,
Arrays.asList(array).forEach(System.out::println);
To print only half the array, I'd suggest copying half the array into a new array using the copyOfRange method,
Integer[] newArray = Arrays.copyOfRange(array, 0, array.length/2);
Arrays.asList(newArray).forEach(System.out::println);
EDIT: Like Marko Topolnik pointed out, we're actually starting out with an array of primitive types instead of object types, so in order to use the asList method we're going to have to convert the array into an array of objects (from int to Integer using Integer[] integerArray = ArrayUtils.toObject(array);). However this just seems tedious/inefficient and OP asked for a shorter way so my suggestion would be to use Marko's method,
Arrays.stream(array).limit(array.length/2).forEach(System.out::println);
EDIT 2: Like Amber Beriwal pointed out, it should be noted that although the one-line solution above looks pretty due to its conciseness, it is still very inefficient/slow compared to the OP's original method. Therefore, I would like to reiterate Amber's comments that the OP and others should just stick with the original for-loop.
for (int i = 0; i < array.length/2; i++)
{
System.out.println(array[i]);
}
How about:
IntStream.range(0, array.length / 2).map(i -> array[i]).forEach(System.out::println);
One line, and no array copies.
Broken down:
IntStream.range(0, array.length / 2) //get the range of numbers 0 - (array length)/2
.map(i -> array[i]) //map from index to value
.forEach(System.out::println); //print result
The answer you have posted is good. Although, I couldn't find a better way to make it compact keeping the performance same, but performance can be improved. Remember following practices while coding:
Algorithm's memory requirement should be optimum
Algorithm's time i.e. performance should be optimum
Algorithm's complexity should not be too much. For significant gains in 1 & 2, this can be skipped.
Considering 1 & 2, lines of code comes at least priority.
Solution 1: This solution will be 4-5 times slower than your approach, plus Stream will take extra space.
Arrays.stream(array).limit(array.length/2).forEach(System.out::println);
Solution 2: This solution is faster than the above code and your code (based on my testing), but Stream will take extra space. Also, it is not compact.
Arrays.stream(array).limit(array.length / 2).forEach(new IntConsumer() {
#Override
public void accept(int value) {
System.out.println(value);
}
});
Solution 3: As suggested by you.
int[] array = new int[] { 0, 1, 2, 3, 4, 5 };
int limit = array.length / 2;
for (int i = 0; i < limit; i++) {
System.out.println(array[i]);
}
Recommendation: Don't go over to reduce the LOC at the stake of losing performance and memory. It is better to keep up with the solution that gives you best performance..
I am trying to generate random array of integers using new Stream API in Java 8. But I haven't understood this API clearly yet. So I need help. Here is my code.
Random random = new Random();
IntStream intStream = random.ints(low, high);
int[] array = intStream.limit(limit) // Limit amount of elements
.boxed() // cast to Integer
.toArray();
But this code returns array of objects. What is wrong with it?
If you want primitive int values, do not call IntStream::boxed as that produces Integer objects by boxing.
Simply use Random::ints which returns an IntStream:
int[] array = new Random().ints(size, lowBound, highBound).toArray();
To generate random numbers from range 0 to 350, limiting the result to 10, and collect as a List. Later it could be typecasted.
However, There are no guarantees on the type, mutability, serializability, or thread-safety of the List returned.
List<Object> numbers = new Random().ints(0,350).limit(10).boxed().collect(Collectors.toList());
and to get thearray of int use
int[] numbers = new Random().ints(0,350).limit(10).toArray();
There's no reason to boxed(). Just receive the Stream as an int[].
int[] array = intStream.limit(limit).toArray();
tl;dr
ThreadLocalRandom // A random number generator isolated to the current thread.
.current() // Returns the current thread's `ThreadLocalRandom` object.
.ints( low , high ) // Pass the "origin" (inclusive) and "bound" (exclusive).
.limit( 100 ) // How many elements (integers) do you want in your stream?
.toArray() // Convert the stream of `int` values into an array `int[]`.
ThreadLocalRandom
You can do it using ThreadLocalRandom.
The ints method generates an IntStream within your specified bounds. Note the the low is inclusive while the high is exclusive. If you want to include your high number, just add one while calling the ints method.
int[] randInts = ThreadLocalRandom.current().ints( low , high ).limit(100).toArray();
See this code run live at IdeOne.com.