8

Lets say I have the following three arrays:

int r[] = {255,255,255};
int g[] = {0,0,0};
int b[] = {255,255,255};

All arrays will have same length.

I want to convert them into an array of objects of type Color:

public class Color {
   int r,g,b;

   public Color(int r, int g, int b) {
        this.r = r;
        this.g = g;
        this.b = b;
   }

}

Color[] arr = new Color[3];

Where each index will contain the r,g,b from the same index from the 3 arrays. For example, lets say for Color[1] = new Color(r[1],g[1],b[1]);

How do I do that using Java Streams ?

The for-loop variant of the code is:

Color arr[] = new Color[r.length];

for(int i=0;i<r.length;i++) {
    Color c = new Color(r[i],g[i],b[i]);
    arr[i] = c;
}

Is there even a way to do this using streams ?

Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
ng.newbie
  • 2,807
  • 3
  • 23
  • 57
  • What you're really looking for is a `zip` operation on streams, which [was considered but proved impractical](https://stackoverflow.com/questions/17640754/zipping-streams-using-jdk8-with-lambda-java-util-stream-streams-zip) because of complications with parallel and potentially-infinite streams. There's not much advantage to using streams instead of the for loop; the loop is more efficient and just as readable. – chrylis -cautiouslyoptimistic- May 22 '20 at 19:05
  • @chrylis-cautiouslyoptimistic- I dont know about you but Ousmane D's answer, is pretty nice. – ng.newbie May 22 '20 at 19:19
  • 1
    @chrylis-cautiouslyoptimistic- even if there was a `zip` for primitive types or even object types for that matter.... I don't think it would be any better than any of the suggested answers for this specific type of problem mainly because `zip` operates on _two sources_ at a time and as you can see in OPs post ideally a solution where you can operate on all sources in a single function would be much better. – Ousmane D. May 22 '20 at 21:31
  • 1
    @chrylis-cautiouslyoptimistic- of-course if there was a `zip` operation (for primitives) for _three_ sources or more then that would definitely be my go to for OPs problem at hand! :). but yes I agree OPs approach is definitely the idiomatic **non-functional** way of doing it. – Ousmane D. May 22 '20 at 21:37

3 Answers3

6

Arrays::setAll

Demo:

import java.awt.Color;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int r[] = { 255, 255, 255 };
        int g[] = { 0, 0, 0 };
        int b[] = { 255, 255, 255 };
        Color[] arr = new Color[3];

        Arrays.setAll(arr, i -> new Color(r[i], g[i], b[i]));
        System.out.println(Arrays.toString(arr));
    }
}

Alternatively, you can use IntStream:range to iterate and fill arr.

import java.awt.Color;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
       int r[] = { 255, 255, 255 };
       int g[] = { 0, 0, 0 };
       int b[] = { 255, 255, 255 };
       Color[] arr = new Color[3];

       IntStream.range(0, arr.length).forEach(i->arr[i] = new Color(r[i], g[i], b[i]));
    }
}
aran
  • 10,978
  • 5
  • 39
  • 69
Arvind Kumar Avinash
  • 71,965
  • 6
  • 74
  • 110
6

IntStream.range + mapToObj then accumulate to an array:

IntStream.range(0, r.length)
         .mapToObj(i -> new Color(r[i], g[i], b[i]))
         .toArray(Color[]::new);
Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
3

You can use IntStream.range for index sequence, and then use map for mapping into Color object and finally collect them into array

IntStream.range(0,r.length)
         .boxed()
         .map(i->new Color(r[i],g[i],b[i]))
         .toArray(Color[]::new);
Ryuzaki L
  • 37,302
  • 12
  • 68
  • 98
  • What is the difference between your answer and Ousmane D.'s answer ? – ng.newbie May 22 '20 at 18:25
  • And why are you using `boxed()` ? Why is that necessary ? – ng.newbie May 22 '20 at 18:26
  • 3
    @ng.newbie not much difference TBH. they're both valid but `mapToObj` is the idiomatic approach in scenarios like this (i.e where you want to map from primitive to object type). basically to convert a stream of primitives to a `Stream`. – Ousmane D. May 22 '20 at 18:27
  • 2
    @OusmaneD. and `mapToObj` is more efficient, as `.boxed() .map(i->new Color(r[i], g[i], b[i]))` will convert all `int` to `Integer` first, to unbox them again, as it is equivalent to `.boxed() .map((Integer i) -> new Color(r[i.intValue()], g[i.intValue()], b[i.intValue()]))` whereas `.mapToObj(i -> new Color(r[i], g[i], b[i]))` truly is `.mapToObj((int i) -> new Color(r[i], g[i], b[i]))` – Holger May 24 '20 at 12:00
  • Thanks @Holger for the feedback, Actually my intention was to use `mapToObj` but i did answered another question using `mapToObj` then my answer keep getting downvotes and recommended to use `boxed()`, can you please correct if my understanding is wrong or if there is something deeper i have to understand https://stackoverflow.com/a/61412082/9959152 – Ryuzaki L May 24 '20 at 16:35