Speaking to people about this around the office made me realise that without the context of how I got here, their response is always "You're doing it wrong", so here's the context:
I've got to deal with crappy objects created by another team using ICE (https://doc.zeroc.com/display/Ice37/Ice+Overview) and wanted to write a utility for the boiler plate code I've been stuck writing.
One of the objects I've got to deal with is a container for floats and strings. I just want the float values.
public class FloatWrapper {
public String StringValue;
public float FloatValue;
public FloatWrapper(float f) {
this.FloatValue = f;
}
public FloatWrapper(String s) {
this.StringValue = s;
}
public FloatWrapper(float f, String s) {
this.FloatValue = f;
this.StringValue = s;
}
}
This container is delivered to me in a 2D array so I want a float[][]. Let's start with 1 dimension
FloatWrapper[] wrappers = new FloatWrapper[] {
new FloatWrapper(1.0f),
new FloatWrapper(2.0f)
};
Float[] asFloats = Arrays.stream(wrappers)
.map(f -> f.FloatValue)
.toArray(Float[]::new);
That was easy. Let's try a 2D array.
// Make a lovely method for what I have above
public Float[] convert1Darray(FloatWrapper[] wrappers) {
return Arrays.stream(wrappers)
.map(f -> f.FloatValue)
.toArray(Float[]::new);
}
public static Float[][] convert2Darray(FloatWrapper[][] wrappers) {
return Arrays.stream(wrappers)
.map(f -> convert1Darray(f))
.toArray(Float[][]::new);
}
...
// Create a 2D array
FloatWrapper[][] wrappers2d = new FloatWrapper[][] {
{ new FloatWrapper(1.0f), new FloatWrapper(2.0f) },
{ new FloatWrapper(3.0f), new FloatWrapper(4.0f) }
};
Float[][] floats2d = convert2Darray(wrappers2d);
Boom!
It'd be nice to have primitive floats (it just would, ok!) The wizened among you can see where I'm going...
Turns out you can't use primitive floats very easily in the 'toArray' call. Apache commons will sort that
public static float[] convert1DprimitiveArray(FloatWrapper[] wrappers) {
return ArrayUtils.toPrimitive(
Arrays.stream(wrappers)
.map(f -> f.FloatValue)
.toArray(Float[]::new));
}
public static float[][] convert2DprimitiveArray(FloatWrapper[][] wrappers) {
return Arrays.stream(wrappers)
.map(f -> convert1DprimitiveArray(f))
.toArray(float[][]::new);
}
.... wait a minute. Why can I use 'toArray(float[][]::new)' but not 'toArray(float[]::new)' ?
The argument floating around the office right now is that the 'float[]::new' call is illegal because you can't call 'new' on a primitive.
But that's not what it's doing. That's just shorthand for the IntFunction
Let's break it down
This:
Arrays.stream(wrappers1d).map(f -> f.FloatValue).toArray(Float[]::new);
is the equivalent of this:
Arrays.stream(wrappers1d).map(f -> f.FloatValue).toArray(new IntFunction<Float[]>() {
@Override
public Float[] apply(int size) {
return new Float[size];
}
});
So, is it that you can't create something of type IntFuntion ??
Nope. Totally allowed:
IntFunction<float[]> happyToCompile = new IntFunction<float[]>() {
@Override
public float[] apply(int size) {
return new float[size];
}
};
Executing that function leaves you with the equivalent of
new float[]{ 0.0f, 0.0f };
So, my half-educated guess is that there's some type inference going on from the IntFunction which works when you have a 2d array but not a 1d array. But, 'could' it work?
Streams primarily work with collections of objects and not primitive types. Fortunately, to provide a way to work with the three most used primitive types – int, long and double – the standard library includes three primitive-specialized implementations: IntStream, LongStream, and DoubleStream.
of() needs flattening whereas Arrays. stream() does not: As the ideal class used for processing of Streams of primitive types are their primitive Stream types (like IntStream, LongStream, etc).
Below are various methods to convert Stream into an Array: Using toArray(): Stream provides toArray() method that returns an array containing the elements of the stream in the form of Object array.
The stream(T[] array) method of Arrays class in Java, is used to get a Sequential Stream from the array passed as the parameter with its elements. It returns a sequential Stream with the elements of the array, passed as parameter, as its source.
Why can I use 'toArray(float[][]::new)' but not 'toArray(float[]::new)' ?
As you said, Arrays.stream(wrappers).map(f -> f.FloatValue)
is a Stream<Float>
so toArray(float[]::new)
does not work as you are required to provide an IntFunction<Float[]>
, not an IntFunction<float[]>
.
On the other hand, Arrays.stream(wrappers).map(f -> convert1DprimitiveArray(f))
is a Stream<float[]>
so toArray(IntFunction<A[]>)
requires an IntFunction<float[][]>
which is what float[][]::new
is for.
.toArray(float[]::new)
would work if you had a Stream<float>
, which is not possible since you can't have a primitive type as a generic parameter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With