Java流:使用基本数据类型的toArray方法

11

在办公室里与人谈论这件事让我意识到,如果不知道我是怎么做到的背景信息,他们的反应总是"你做错了",所以这就是背景:

我必须处理由另一个团队使用ICE (https://doc.zeroc.com/display/Ice37/Ice+Overview)创建的垃圾对象,并想为我一直被卡住写的样板代码编写实用程序。

我要处理的对象之一是包含浮点数和字符串的容器。我只想要浮点值。

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;
    }
}

这个容器是以二维数组(2D array)的形式交付给我的,因此我想要一个浮点数二维数组(float[][])。让我们从一维数组开始。

FloatWrapper[] wrappers = new FloatWrapper[] { 
    new FloatWrapper(1.0f), 
    new FloatWrapper(2.0f) 
};

Float[] asFloats = Arrays.stream(wrappers)
    .map(f -> f.FloatValue)
    .toArray(Float[]::new);

那很容易。我们来试试二维数组。

// 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);

砰!

如果有原始的浮点数会很好(就是这样,好吧!),你们中有经验的人可以看出我要说什么...

事实证明,在 "toArray" 调用中不能很容易地使用原始的浮点数。Apache Commons 可以解决这个问题。

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);
}

……等一下。为什么我可以使用'toArray(float[][]::new)'但不能使用'toArray(float[]::new)'?

办公室里流传的说法是,'float[]::new'调用是不合法的,因为你不能在原始类型上调用'new'。

但那不是它正在做的事情。那只是IntFunction的简写。

让我们分解一下:

这个:

Arrays.stream(wrappers1d).map(f -> f.FloatValue).toArray(Float[]::new);
这相当于这个:
Arrays.stream(wrappers1d).map(f -> f.FloatValue).toArray(new IntFunction<Float[]>() {
    @Override
    public Float[] apply(int size) {
        return new Float[size];
    }
});

那么,是不是你不能创建IntFuntion类型的东西呢?

不是的。完全允许:

IntFunction<float[]> happyToCompile = new IntFunction<float[]>() {
    @Override
    public float[] apply(int size) {
        return new float[size];
    }
};

执行该函数会使您得到相当于

new float[]{ 0.0f, 0.0f };

所以,我的半猜测是从IntFunction推断出某种类型,在二维数组中运作但在一维数组中不起作用。但是,“可能”会有用吗?

3个回答

7
为什么可以使用“toArray(float [] [] :: new)”,但无法使用“toArray(float [] :: new)”?
如您所说,“Arrays.stream(wrappers)。map(f -&gt; f.FloatValue)”是一个Stream ,因此toArray(float [] :: new)不起作用,因为您需要提供一个IntFunction ,而不是IntFunction 。
另一方面,“Arrays.stream(wrappers)。map(f -&gt; convert1DprimitiveArray(f))”是一个Stream ,因此toArray(IntFunction )需要一个IntFunction ,这就是“float [] [] :: new”的作用。
如果您有一个Stream ,那么“.toArray(float [] :: new)”将起作用,但由于您不能将原始类型作为泛型参数,因此不可能拥有该类型的Stream 。

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

2
因为在map调用之后,你得到的是一个数组流,要创建一个数组流,你需要创建一个数组的数组,而不是float类型的数组。如果要获取基本类型,你需要先转换成DoubleStream。
Arrays.stream(wrappers).mapToDouble(FloatWrapper::getFloat)

该内容提供了一系列基本类型;你应该能够通过一些额外的技巧将其转换为浮点数数组。


2
那个错误是因为没有包装流来处理float。所以你不能使用FloatStream或类似的东西。因此,当你分析Stream.toArray()的真正含义时,你会得到以下内容:返回一个数组T[],其中包含该流中类型为T的每个对象。问题在于T,因为你有一个包含float的流,存储在它们的WrapperClass Float中(因为原始类型不能与泛型一起使用)。现在,当你尝试将Float转换为float时,Java会自动进行拆箱。不好的是,它对数组不起作用。在Java中,原始类型的数组不能直接转换为其特定的Wrapper类型。因此,Float[] floats = new float[10];会产生编译错误。
现在你会问自己:那么为什么float[][]可以工作呢?好吧,如果你看一下它,你会发现float[]是一个Object,并且float数组流是有效的,因为它们可以被引用为一个对象。这就是为什么float[][]::new起作用的原因。它符合之前的描述:返回一个包含此流中每个类型为T的对象的数组T[],因为float[]实际上是T类型的,而T[]也是有效的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接