如何从流中获取可观察的浮点数组?

时间:2014-11-15 22:24:17

标签: java-8 javafx-8

这与one几乎相同,但方向相反。

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

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

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

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

可以使用ObservableFloatArrayFXCollections.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};
    }
}

在执行一些计算后,我有List<Vertex> listVertices。我需要生成float[] arrayVertices才能最终调用triangleMesh.getPoints().addAll(arrayVertices);

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

listVertices.forEach(vertex->triangleMesh.getPoints().addAll(vertex.getCoordenates()));

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

如果FloatStreamflatMapToFloat()存在,我会做这样的事情:

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

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

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

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

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

2 个答案:

答案 0 :(得分:6)

请记住,Stream定义操作而不是存储。因此,对于大多数操作,当使用CPU寄存器时,使用float仅比double值提供的好处很少。对于可以使用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;
    }
}

这支持并行处理,但是,对于仅包含数据复制的操作,并行处理没有任何好处。

答案 1 :(得分:2)

我认为您不得不创建一个数据结构(例如double[]List<Float>)然后将其映射到float[]这一事实。 (但也许我错过了一些东西。)

如果您想使用类似Stream的API执行此操作,可以使用Collector在最后执行映射:

import java.util.List;
import java.util.ListIterator;
import java.util.Random;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import javafx.scene.shape.TriangleMesh;

public class StreamFloatsTest {

    public static void main(String[] args) {

        // The following declaration works in Eclipse 4.4
        // however it won't compile from the command line:
        // Collector<Float, List<Float>, float[]> toFloatArray =

        // This declaration works:

        Collector<Float, ?, float[]> toFloatArray =
                Collectors.collectingAndThen(Collectors.toList(), floatList -> {
                    float[] array = new float[floatList.size()];
                    for (ListIterator<Float> iterator = floatList.listIterator(); iterator.hasNext();) {
                        array[iterator.nextIndex()] = iterator.next();
                    }
                    return array ;
                });


        // Random vertex list for demo purposes:
        Random rng = new Random();
        List<Vertex> vertices = IntStream.range(0, 100)
                .mapToObj(i -> new Vertex(rng.nextFloat(), rng.nextFloat(), rng.nextFloat()))
                .collect(Collectors.toList());

        float[] vertexArray = vertices.stream()
                .flatMap(v -> Stream.of(v.getX(), v.getY(), v.getZ()))
                .collect(toFloatArray);


        TriangleMesh mesh = new TriangleMesh();

        mesh.getPoints().addListener(obs -> System.out.println("mesh invalidated"));
        mesh.getPoints().addListener((array, sizeChanged, from, to) -> System.out.println("mesh changed"));
        mesh.getPoints().addAll(vertexArray);

    }

    public static 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 getX() {
            return x;
        }
        public float getY() {
            return y;
        }
        public float getZ() {
            return z;
        }
        public float[] getCoordinates() {
            return new float[] {x, y, z};
        }
    }
}