使用数组的 JavaFX 折线图

JavaFX line chart using arrays

我想使用 JavaFX 绘制折线图。我如何使用数组数据集进行绘图,这意味着我有两个等长的双精度 X 和 Y 数组。我想将它们用作绘图的数据集。

提前致谢。

只需循环填充系列:

final NumberAxis xAxis = new NumberAxis();
final NumberAxis yAxis = new NumberAxis();

LineChart<Number,Number lineChart = new LineChart<Number,Number>(xAxis,yAxis);

XYChart.Series series = new XYChart.Series();
series.setName("data");
for(int i = 0; i<xArray.length; i++) {
    series.getData().add(new XYChart.Data(xArray[i], yArray[i]);
}
lineChart.getData().add(series);

编辑:我的代码中有一个不必要的步骤。将数据添加到系列的改进性能并不是直接归因于集合与 ObservableList,当我创建 ObservableArrayList 并直接用数据填充 ObservableArrayList 时,我获得了相同的性能。

导致 Christian Fries 解决方案对大型数据集花费更长时间的问题在 XYChart.Series class 中。如果您查看 XYChart.Series 的 JavaFX 源代码,它有一个遍历所有数据的更改侦听器,并且在每次向系列添加数据时激活。

长话短说,您可以从我的原始解决方案中删除 1 行,如以下代码所示,并获得相同的性能改进。但是,我会保留我的原件。

    // Create the graph
    LineChart<Number, Number> graph = new LineChart<Number, Number>(new NumberAxis(), new NumberAxis());
    
    // Create the list
    ObservableList<XYChart.Data<Number, Number>> list = FXCollections.observableArrayList();
    
    // Add all values from the array into the list
    for (int val : value)
        list.add(new XYChart.Data<Number, Number>(val,val));
    
    // create a series from the list
    XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>(list);
    
    // Add the series to the graph
    graph.getData().add(series);

我一直在寻找这个问题的解决方案,但 Christian Fries 的解决方案对我不起作用,因为我的数据集非常大,而且绘制图表的时间太长了。

经过一些研究,我发现这个问题可能是由于 JavaFX 系列 class 的工作方式造成的,并且每次向系列添加数据时,它实际上都必须重新评估所有数据的有效性,因为它在内部使用了一个 ObservableList,所以每个添加的数据点都比最后一个花费更长的时间。换句话说,指数时间复杂度绝对是要避免的。

我发现一个更好的解决方案是创建一个 JavaFX 数据集合,并将数据添加到该集合中。一旦完全填充,这个集合就可以变成一个 JavaFX 系列,并像通常绘制系列图一样绘制图形。见以下代码,其中数组命名为“value”。

    // Create the graph
    LineChart<Number, Number> graph = new LineChart<Number, Number>(new NumberAxis(), new NumberAxis());
    
    // Create the collection
    Collection<XYChart.Data<Number, Number>> collection = new ArrayList<XYChart.Data<Number, Number>>();
    
    // Put all values from the array into the collection
    for (int val : value)
        collection.add(new XYChart.Data<Number, Number>(val,val));
    
    // Create the data using the collection
    ObservableList<XYChart.Data<Number, Number>> list = FXCollections.observableArrayList(collection);
    
    // create a series from the list
    XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>(list);
    
    // Add the series to the graph
    graph.getData().add(series);

虽然代码看起来比 Christian Fries 稍微复杂一些,但实际上它在性能方面要高效得多。如果你想看到实际的性能差异,你可以 运行 下面的代码,但我不推荐它,因为方法 #2 需要很长时间。另外,请记住,代码中的第二种方法具有指数时间复杂度,因此数据越多,所需时间的差异就越大。

    import java.util.ArrayList;
    import java.util.Calendar;
    import java.util.Collection;
    
    import javafx.application.Application;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.scene.Scene;
    import javafx.scene.chart.LineChart;
    import javafx.scene.chart.NumberAxis;
    import javafx.scene.chart.XYChart;
    import javafx.stage.Stage;
    
    public class GraphingImplementation  extends Application
    {
        @Override
        public void start(Stage stage)
        {
            // close the initial stage.
            stage.close();
            
            // The size of the array
            int seriesSize = 50000;
            
            // Build the graphs
            LineChart<Number, Number> graph1 = new LineChart<Number, Number>(new NumberAxis(), new NumberAxis());
            LineChart<Number, Number> graph2 = new LineChart<Number, Number>(new NumberAxis(), new NumberAxis());
            
            // Deactivate features that cause slow down
            graph1.setCreateSymbols(false);
            graph1.setAnimated(false);
            graph2.setCreateSymbols(false);
            graph2.setAnimated(false);
            
            // Create value array to graph.
            int value[] = new int[seriesSize];
            for(int i = 0; i<seriesSize; i++)
                value[i] = i;
            
            
            
            //**********************************************************
            // METHOD #1 - Creation of collection to populate series
            //**********************************************************
            // TIME COMPLEXITY - O{n}
            //**********************************************************
            // Mark the starting time of graphing method #1
            long startTime = Calendar.getInstance().getTimeInMillis();
            // Create the collection
            Collection<XYChart.Data<Number, Number>> collection = new ArrayList<XYChart.Data<Number, Number>>();
            // Add all values into the collection
            for (int val : value)
                collection.add(new XYChart.Data<Number, Number>(val,val));
            // Create the data using the collection
            ObservableList<XYChart.Data<Number, Number>> list = FXCollections.observableArrayList(collection);
            // create a series from the list
            XYChart.Series<Number, Number> series = new XYChart.Series<Number, Number>(list);
            // Add the series to the graph
            graph1.getData().add(series);
            // Mark the stopping time of graphing method #1
            long stopTime = Calendar.getInstance().getTimeInMillis();
            // Print the run time for method #1
            System.out.println((stopTime - startTime) + " Milliseconds");
            
            
            
            // build the first scene
            Scene scene1 = new Scene(graph1, 640, 480);
            // create and populate the first stage
            Stage stage1 = new Stage();
            stage1.setScene(scene1);
            // show the first stage
            stage1.show();
            
            
    
            
            //**********************************************************
            // METHOD #2 - Direct creation of a series
            //**********************************************************
            // TIME COMPLEXITY - O{n^2}
            //**********************************************************
            // Mark the starting time of graphing method #2
            startTime = Calendar.getInstance().getTimeInMillis();
            // Create the XYChart Series
            XYChart.Series<Number, Number> series2 = new XYChart.Series<Number, Number>();
            // add all values to the series
            for (long val : value)
                series2.getData().add(new XYChart.Data<Number, Number>(val, val));
            // add the series to the graph
            graph2.getData().add(series2);
            // Mark the stopping time of graphing method #2
            stopTime = Calendar.getInstance().getTimeInMillis();
            // Print the run time for method #2
            System.out.println((stopTime - startTime) + " Milliseconds");
            
            
            
            // build the second scene
            Scene scene2 = new Scene(graph2, 640, 480);
            // create and populate the second stage
            Stage stage2 = new Stage();
            stage2.setScene(scene2);
            // show the second stage
            stage2.show();
        }
        
        public static void main(String[] args)
        {
            launch(args);
        }
        
    }

此代码在终端中报告以下内容:

75 毫秒

89896 毫秒

正如您所看到的,方法 #1 只需要 75 毫秒,而方法 #2 将近一分半钟。此外,您绘制的数据越多,改进方法 #1 的效果就越好。

此外,如果这很重要,请注意。我正在使用 Java/JavaFX 8.