Java 8 如何从流中获取可观测的数据流?

Java 8 如何从流中获取可观测的数据流?,java-8,javafx-8,Java 8,Javafx 8,这几乎是同一个问题,但方向相反 我知道Java 8中没有FloatStream,float[]的用例也不多,但我有一个: 在JavaFX 3D中处理TriangleMesh,必须为整个网格顶点的3D坐标提供ObservableFloatArray 作为一些计算的结果,我将在列表中列出所有这些坐标,若要立即将所有坐标添加到网格中,我将使用以下方法之一调用triangleMesh.getPoints().addAll(): addAll(可观察到的流量src) addAll(浮动…元素) addA

这几乎是同一个问题,但方向相反

我知道Java 8中没有
FloatStream
,float[]的用例也不多,但我有一个:

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

作为一些计算的结果,我将在
列表中列出所有这些坐标
,若要立即将所有坐标添加到网格中,我将使用以下方法之一调用
triangleMesh.getPoints().addAll()

  • addAll(可观察到的流量src)
  • addAll(浮动…元素)
  • addAll(Observalefloatarray src、int-srcdex、int-length)
  • addAll(float[]src,int-srcdex,int-length)
其中可以使用
FXCollections.ObservableFloatArray()
FXCollections.ObservableFloatArray(ObservableFloatArray数组)
FXCollections.ObservableFloatArray(float…value)
创建

假设每个顶点都有一个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};
    }
}
在执行了一些计算之后,我列出了列表顶点。我需要生成
float[]arrayVertices
以最终调用
triangleMesh.getPoints().addAll(arrayVertices)

现在我正在做的就是:

listVertices.forEach(vertex->triangleMesh.getPoints().addAll(vertex.getCoordenates()));
但这会在添加到可观察数组的每个新顶点上触发关联的侦听器,对于大量顶点,这会影响性能

如果
FloatStream
flatMapToFloat()
存在,我会这样做:

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);
但据我所知,没有办法使用流


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

我认为没有任何方法可以绕过这样一个事实:您必须创建一个数据结构(例如,一个
双[]
或一个
列表
),然后将其映射到
浮点[]
。(但也许我遗漏了什么。)

如果要使用类似于API的
流执行此操作,可以使用
收集器
在末尾执行映射:

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};
        }
    }
}
import java.util.List;
导入java.util.ListIterator;
导入java.util.Random;
导入java.util.stream.Collector;
导入java.util.stream.collector;
导入java.util.stream.IntStream;
导入java.util.stream.stream;
导入javafx.scene.shape.TriangleMesh;
公共类FloatsTest{
公共静态void main(字符串[]args){
//以下声明适用于Eclipse4.4
//但是,它不会从命令行编译:
//托福雷收集器=
//这一宣言的作用是:
托福雷收集器=
Collectors.collectionAndThen(Collectors.toList(),floatList->{
float[]数组=新的float[floatList.size()];
for(ListIterator迭代器=floatList.ListIterator();迭代器.hasNext();){
数组[iterator.nextIndex()]=iterator.next();
}
返回数组;
});
//用于演示目的的随机顶点列表:
随机rng=新随机();
列表顶点=IntStream.range(0,100)
.mapToObj(i->新顶点(rng.nextFloat(),rng.nextFloat(),rng.nextFloat())
.collect(Collectors.toList());
float[]vertexArray=顶点。stream()
.flatMap(v->Stream.of(v.getX(),v.getY(),v.getZ())
.收集(toFloatArray);
三角形网格=新三角形网格();
mesh.getPoints().addListener(obs->System.out.println(“mesh无效”));
mesh.getPoints().addListener((数组,sizeChanged,from,to)->System.out.println(“mesh已更改”);
mesh.getPoints().addAll(vertexArray);
}
公共静态类顶点{
私人最终浮动x;
私人最终浮动;
私人最终浮动z;
公共顶点(浮动x、浮动y、浮动z){
这个.x=x;
这个。y=y;
这个。z=z;
}
公共浮点getX(){
返回x;
}
公共浮球{
返回y;
}
公共浮动getZ(){
返回z;
}
公共浮点[]getCoordinates(){
返回新的浮点[]{x,y,z};
}
}
}

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

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

谢谢,@James\u D,我已经让它工作了。唯一的问题是我不能使用
收集器
。它说“收集的类型(收集器,函数)是错误的”。但这是有效的
收集器
。(使用NetBeans 8.0.1、JDK8u25、Windows7 64位)很有趣。这在我的IDE(Eclipse4.4;JDK8U40,MacOSX10.9.5)中编译并运行良好-但是。。。它不会从命令行编译,除非我在您的注释中做了更改。(如果我使用Eclipse来编译它,它可以从命令行运行,这并不奇怪。)我想我应该更新从命令行编译的内容的答案,尽管我很想知道差异来自何处。(看起来它应该能够正确推断供应商类型,就像在Eclips中一样