使用数组的 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.
我想使用 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.