如何从Stream获取ObservableFloatArray?

Jos*_*eda 5 java-8 javafx-8

这几乎是因为这同样的问题一个,但在相反的方向.

我知道FloatStreamJava 8中没有,并且float []没有很多用例,但我有一个:

TriangleMesh在JavaFX 3D中处理时,必须提供ObservableFloatArray整个网格顶点的3D坐标.

作为一些计算的结果,我将在a中具有所有这些坐标List,并且将所有这些坐标一次性添加到网格中,我将triangleMesh.getPoints().addAll()使用以下方法之一调用:

  • addAll(ObservableFloatArray src)
  • addAll(float ... elements)
  • addAll(ObservableFloatArray src,int srcIndex,int length)
  • addAll(float [] src,int srcIndex,int length)

其中ObservableFloatArray可使用被创建FXCollections.observableFloatArray(),FXCollections.observableFloatArray(ObservableFloatArray array)FXCollections.observableFloatArray(float... values).

假设我为每个顶点都有这个pojo:

private class Vertex {
    private final float x;
    private final float y;
    private final float z;

    public Vertex(float x, float y, float z){
        this.x=x; this.y=y; this.z=z;
    }

    public float[] getCoordenates(){
        return new float[]{x,y,z};
    }
}
Run Code Online (Sandbox Code Playgroud)

我做了一些计算后List<Vertex> listVertices.我需要生成float[] arrayVertices最终调用triangleMesh.getPoints().addAll(arrayVertices);.

现在这就是我正在做的事情:

listVertices.forEach(vertex->triangleMesh.getPoints().addAll(vertex.getCoordenates()));
Run Code Online (Sandbox Code Playgroud)

但是这会在添加到可观察数组的每个新顶点上触发关联的侦听器,对于大量顶点,这会影响性能.

应该FloatStreamflatMapToFloat()我的存在,会做这样的事情:

float[] arrayVertices = listVertices.stream()
            .map(vertex->FloatStream.of(vertex.getCoordenates()))
            .flatMapToFloat(f->f).toArray();
triangleMesh.getPoints().addAll(arrayVertices);
Run Code Online (Sandbox Code Playgroud)

就像我实际使用面部索引的int []列表一样:

int[] arrayFaces = listFaces.stream()
            .map(face->IntStream.of(face.getFaceIndices()))
            .flatMapToInt(i->i).toArray();
triangleMesh.getFaces().addAll(arrayFaces);
Run Code Online (Sandbox Code Playgroud)

但据我所知,没有办法使用流.

提前感谢任何涉及流的可能解决方案.

Hol*_*ger 7

请记住,a Stream定义操作而不是存储.因此,对于大多数操作,当使用CPU寄存器时,使用a floatdouble比值提供的好处很少.对于可以使用SSE或GPU加速的操作,可能会有理论上的改进,但这与此无关.

所以,你可以使用DoubleStream该操作,你唯一需要的是能够收集的收集DoubleStream到一个float[]数组:

float[] arrayVertices = listVertices.stream()
    .flatMapToDouble(vertex->DoubleStream.of(vertex.x, vertex.y, vertex.z))
    .collect(FaCollector::new, FaCollector::add, FaCollector::join)
    .toArray();

static class FaCollector {
    float[] curr=new float[64];
    int size;
    void add(double d) {
        if(curr.length==size) curr=Arrays.copyOf(curr, size*2);
        curr[size++]=(float)d;
    }
    void join(FaCollector other) {
        if(size+other.size > curr.length)
            curr=Arrays.copyOf(curr, size+other.size);
        System.arraycopy(other.curr, 0, curr, size, other.size);
        size+=other.size;
    }
    float[] toArray() {
        if(size!=curr.length) curr=Arrays.copyOf(curr, size);
        return curr;
    }
}
Run Code Online (Sandbox Code Playgroud)

这支持并行处理,但是,对于仅仅由数据复制组成的操作,并行处理没有任何好处.

  • 另一个解决方案还必须处理未知数量的项目,但它解决这个问题的方式隐藏在`Collectors.toList()`部分.但它在将整个数据复制到执行拆箱操作的`float []`数组之前,将数据收集到`Float`实例的`List`中.没有理想的初始数组大小; 这总是一种权衡.但是,您可以使用initialSize提示向`FaCollector`添加构造函数,并将`FaCollector :: new`更改为`() - > new FaCollector(hint)`以提供特定于应用程序的提示值,例如`listVertices.size ()*3`用于顺序收集 (3认同)
  • @James_D:是的,这就是`Stream.collect`的用途.并发收集将使用`Supplier`为每个线程创建一个实例,因此*accumulator*`add`方法的并发调用不会干扰(如果它的副作用仅限于它自己的实例)和*combiner*`当两个线程完成其实例时,将调用join`.对于有序流,实现还将确保实例以维护顺序组合. (3认同)
  • @James_D:或者[用规范使用的简短字](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#collect-java.util. function.Supplier-java.util.function.BiConsumer-java.util.function.BiConsumer-):"像reduce ...,可以并行化收集操作而无需额外的同步"另请参见http://stackoverflow.com/q/22350288/2711488 (3认同)