8

Starting with Java 8 so need a bit of time to get used to it. It's a classical problem, I've an array of objects that I want to transform.

Before Java8 the ideal code would be (no null pointers):

P[] outputArray = new P[inputArray.length];
for (int i =0; i< inputArray.length; i++ )
{
    outputArray [i] = inputArray[i].transformToP();
}

What is the best version in Java8 ?

ic3
  • 7,917
  • 14
  • 67
  • 115

2 Answers2

12

Using the Stream API it's quite simple:

P[] outputArray = Arrays.stream(inputArray).map(e -> e.transformToP()).toArray(P[]::new);

Also method reference can be used (suppose that I is the type of input elements):

P[] outputArray = Arrays.stream(inputArray).map(I::transformToP).toArray(P[]::new);

Note that you may have problems if transformToP() method throws checked exceptions. In this case convert them to unchecked ones or consult this question.

Community
  • 1
  • 1
Tagir Valeev
  • 97,161
  • 19
  • 222
  • 334
7

Using a stream over an array is a fine technique as described in Tagir Valeev's answer. However, don't forget about Arrays.setAll. This is a handy shortcut for setting all the elements of an array based on index. To transform an array to a new array by some function, you could do this:

P[] outputArray = new P[inputArray.length];
Arrays.setAll(outputArray, i -> inputArray[i].transform());

You don't have to copy it into a new array. If you want to transform the array in-place, you could do this:

Arrays.setAll(array, i -> array[i].transform());

There is also a parallel variation parallelSetAll.

Under the covers this is just an IntStream.range over the indexes of the input array, but it's sometimes darned convenient for quick one-liners.

Community
  • 1
  • 1
Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
  • Nice addition, upvote. Though OP's `transformToP` suggests that `outputArray` type `P` differs from `inputArray` type. By the way it would be very useful to have something like `Arrays.replaceAll(array, fn)` which passes to the `fn` not the array index, but the current value, so you can write `Arrays.replaceAll(array, P::transform);` – Tagir Valeev Aug 15 '15 at 02:23
  • @TagirValeev Thanks. There indeed are a bunch of things "missing" from `Arrays` that would be quite useful. The problem is that `Arrays` is already too cluttered and any addition would also imply adding subrange variations and primitive specializations. – Stuart Marks Aug 15 '15 at 16:03
  • I see. I always wonder if it's possible to add such methods directly to the array type (using like `array.setAll(i -> array[i].transform());`). There's already a `clone()` method which is available in array type directly. Why not adding more array-related methods instead of cluttering the `Arrays` class? The primitive specializations would just be located in the corresponding primitive array types. Well, it may require significant changes in the language, but it would make the code much clearer. The javac may just convert such calls to static methods invocations. – Tagir Valeev Aug 15 '15 at 16:20
  • 1
    @TagirValeev Known problem. :-) Unfortunately the `clone()` method and `length` field of arrays are quite special-cased in the language. There are some ideas floating around to make arrays implement some interface, so these things would be declared explicitly, and possibly even extended. See John Rose's "Arrays 2.0" talk at the [2012 JVMLS](http://www.oracle.com/technetwork/java/javase/community/jvmls2012-1840099.html). Note however that although some of this has been prototyped, none of it is committed for JDK 9 as far as I know. – Stuart Marks Aug 17 '15 at 21:45
  • Nice one, for the time being my favorite as it creates less objects (yes, yes sometimes it's important) – ic3 Aug 18 '15 at 08:23