Last active
December 6, 2022 08:15
-
-
Save james-d/8602638 to your computer and use it in GitHub Desktop.
The Bindings.select*(...) family of methods use try-catch to implement null checking of "intermediate" properties. This is highly inefficient, as demonstrated by this example. Sort by the "Zip" column to see the problem.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.Random; | |
import javafx.application.Application; | |
import javafx.beans.binding.Bindings; | |
import javafx.beans.property.ObjectProperty; | |
import javafx.beans.property.ReadOnlyStringWrapper; | |
import javafx.beans.property.SimpleObjectProperty; | |
import javafx.beans.property.SimpleStringProperty; | |
import javafx.beans.property.StringProperty; | |
import javafx.beans.value.ObservableValue; | |
import javafx.collections.FXCollections; | |
import javafx.collections.ObservableList; | |
import javafx.scene.Scene; | |
import javafx.scene.control.TableColumn; | |
import javafx.scene.control.TableView; | |
import javafx.scene.control.TableColumn.CellDataFeatures; | |
import javafx.scene.control.cell.PropertyValueFactory; | |
import javafx.scene.layout.BorderPane; | |
import javafx.stage.Stage; | |
import javafx.util.Callback; | |
public class TableWithSelectBinding extends Application { | |
private static final int NUM_ROWS = 1_000_000 ; | |
private static final Random RNG = new Random(); | |
@Override | |
public void start(Stage primaryStage) { | |
final TableView<Person> table = new TableView<>(); | |
final TableColumn<Person, String> nameCol = new TableColumn<>("Name"); | |
final TableColumn<Person, String> zipCol = new TableColumn<>("Zip"); | |
nameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name")); | |
zipCol.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<Person,String>, ObservableValue<String>>() { | |
@Override | |
public ObservableValue<String> call(CellDataFeatures<Person, String> cdf) { | |
Person person = cdf.getValue(); | |
// Bindings.selectString uses try{}catch(){} for logical checking of null values | |
// This is inefficient enough to hang this example on sorting with 1,000,000 rows | |
return Bindings.selectString(person.addressProperty(), "zip"); | |
// The following is an incomplete implementation | |
// (will not update if Person.addressProperty changes) | |
// but uses proper null checking and consequently runs much faster: | |
// Address address = person.getAddress(); | |
// if (address == null) { | |
// return new ReadOnlyStringWrapper(""); | |
// } else { | |
// return address.zipProperty() ; | |
// } | |
} | |
}); | |
table.getColumns().addAll(nameCol, zipCol); | |
table.setItems(createData()); | |
final BorderPane root = new BorderPane(); | |
root.setCenter(table); | |
final Scene scene = new Scene(root, 600, 400); | |
primaryStage.setScene(scene); | |
primaryStage.show(); | |
} | |
private ObservableList<Person> createData() { | |
List<Person> data = new ArrayList<Person>(); | |
for (int i=0; i<NUM_ROWS; i++) { | |
data.add(new Person(randomString(), null)); | |
} | |
return FXCollections.observableArrayList(data); | |
} | |
private String randomString() { | |
StringBuilder sb = new StringBuilder(); | |
for (int i=0; i<=8; i++) { | |
sb.append((char) ('a' + RNG.nextInt(26))); | |
} | |
return sb.toString(); | |
} | |
public static void main(String[] args) { | |
launch(args); | |
} | |
public static class Person { | |
private final StringProperty name ; | |
private final ObjectProperty<Address> address ; | |
public Person(String name, Address address) { | |
this.name = new SimpleStringProperty(this, "name", name); | |
this.address = new SimpleObjectProperty<Address>(this, "address", address); | |
} | |
public final String getName() { | |
return name.get(); | |
} | |
public final void setName(String name) { | |
this.name.set(name); | |
} | |
public final StringProperty nameProperty() { | |
return name ; | |
} | |
public final Address getAddress() { | |
return address.get(); | |
} | |
public final void setAddress(Address address) { | |
this.address.set(address); | |
} | |
public final ObjectProperty<Address> addressProperty() { | |
return address ; | |
} | |
} | |
public static class Address { | |
private final StringProperty zip ; | |
// other properties omitted | |
public Address(String zip) { | |
this.zip = new SimpleStringProperty(this, "zip", zip); | |
} | |
public final String getZip() { | |
return zip.get(); | |
} | |
public final void setZip(String zip) { | |
this.zip.set(zip); | |
} | |
public final StringProperty zipProperty() { | |
return zip ; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment