Java Streams toArray 与原语

bei*_*pol 7 java arrays generics lambda

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

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);
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

.... 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);
Run Code Online (Sandbox Code Playgroud)

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];
    }
});
Run Code Online (Sandbox Code Playgroud)

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];
    }
};
Run Code Online (Sandbox Code Playgroud)

Executing that function leaves you with the equivalent of

new float[]{ 0.0f, 0.0f };
Run Code Online (Sandbox Code Playgroud)

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?

Ale*_* C. 7

为什么我可以使用 'toArray(float[][]::new)' 而不能使用 'toArray(float[]::new)' ?

正如您所说,Arrays.stream(wrappers).map(f -> f.FloatValue)是 a ,Stream<Float>所以toArray(float[]::new)不起作用,因为您需要提供 a IntFunction<Float[]>,而不是IntFunction<float[]>

另一方面,Arrays.stream(wrappers).map(f -> convert1DprimitiveArray(f))“is a Stream<float[]>so toArray(IntFunction<A[]>)”需要“ IntFunction<float[][]>which is float[][]::newfor”。

.toArray(float[]::new)如果您有 a Stream<float>,则可以工作,这是不可能的,因为您不能将原始类型作为泛型参数。

  • 我想很多人同时回答了这个问题,但这个问题对于我原始的(明白了吗?)大脑来说是最容易理解的。谢谢:) (2认同)