I am developing a JavaFX 2.2 application using Netbeans 7.2. I am working with a treeview and I extended TreeCell to provide to each TreeItem a context-menu with a MenuItem with "Collpase All" functionality. The max depth level of the treeview is 4. When a user right clicks on a TreeItem of level 2 and clicks to "Collapse All" MenuItem i want to make all the TreeItems of level 3 collapsed (setExpanded(false)). Below you can see the code that I am using. My problem is the memory and CPU cost of this operation. I inserted 250 TreeItems to level 3. The cost of a collapse all operation was ~200MB of memory on each collapseAll click and spends about 2s of time! My developer computer's CPU is an Intel i5 (3.3GHz) and I have 8GB of memory. Is this numbers of hardware cost normal or I am doing something wrong in my code? Am I using a wrong way to collapse them?
This class manages the TreeView. Loads data from database, reloads them, knows the selected TreeItem and expand/collapse the selected children TreeItems.
public final class TargetTree extends SqlConnectionManager {
private TreeView tree;
private TreeItem selectedItem;
private TargetTree() {
super();
this.tree = null;
this.selectedItem = null;
}
private TargetTree(TreeView tree) {
super();
this.tree = tree;
this.selectedItem = null;
}
public static TargetTree construct(TreeView tree) {
if (tree == null) {
return null;
}
TargetTree targetTree = new TargetTree(tree);
targetTree.load();
return targetTree;
}
public void reload() {
// Clear current tree.
if (tree.getRoot() != null) {
for (int i = 0; i < tree.getRoot().getChildren().size(); i++) {
tree.getRoot().getChildren().clear();
}
tree.setRoot(null);
}
this.load();
}
public void prune() {
//TODO
}
private void load() {
// New root Item.
final TreeItem<Object> treeRoot = new TreeItem<>((Object) "Root");
treeRoot.setExpanded(true);
// This integers help to find when to build a new department/section/measure.
int lastDepartmentId = -1;
int lastSectionId = -1;
int lastMeasureId = -1;
int lastTargetId = -1;
//The temp treeitems.
TreeItem<Object> departmentTreeItem = null;
TreeItem<Object> sectionTreeItem = null;
TreeItem<Object> measureTreeItem = null;
TreeItem<Object> targetTreeItem = null;
// Get the new TreeItems from the database.
super.errorMessage = "";
try {
// Establishing connection with db.
super.openConnection();
// Query to be executed. Selects everything from the database.
preparedStmt = connection.prepareStatement(
"SELECT.....ORDER BY....;");
resultSet = preparedStmt.executeQuery();
while (resultSet.next()) {
// Department Creation.
if (lastDepartmentId != resultSet.getInt("departmentId")) {
final Department department = Department.initEmpty();
department.setId(resultSet.getInt("departmentId"));
department.setName(resultSet.getString("departmentName"));
// Create the treeitem for this department.
departmentTreeItem = new TreeItem<>((Object) department);
departmentTreeItem.setExpanded(true);
treeRoot.getChildren().add(departmentTreeItem);
// Reset the children ids to ensure that they will be recreated.
lastDepartmentId = resultSet.getInt("departmentId");
lastSectionId = -1;
lastMeasureId = -1;
lastTargetId = -1;
}
// Section Creation.
if (lastSectionId != resultSet.getInt("sectionId")) {
final Section section = Section.initEmpty();
section.setId(resultSet.getInt("sectionId"));
section.setName(resultSet.getString("sectionName"));
// Create the treeitem for this section.
sectionTreeItem = new TreeItem<>((Object) section);
sectionTreeItem.setExpanded(true);
departmentTreeItem.getChildren().add(sectionTreeItem);
// Reset the children ids to ensure that they will be recreated.
lastSectionId = resultSet.getInt("sectionId");
lastMeasureId = -1;
lastTargetId = -1;
}
// Measure Creation.
if (lastMeasureId != resultSet.getInt("measureId")) {
final Measure measure = Measure.initEmpty();
measure.setId(resultSet.getInt("measureId"));
measure.setLastname(resultSet.getString("measureLastname"));
measure.setFirstname(resultSet.getString("measureFirstName"));
// Create the treeitem for this measure.
measureTreeItem = new TreeItem<>((Object) measure);
measureTreeItem.setExpanded(true);
sectionTreeItem.getChildren().add(measureTreeItem );
// Reset the children ids to ensure that they will be recreated.
lastMeasureId = resultSet.getInt("measureId");
lastTargetId = -1;
}
// Target Creation.
if (lastTargetId != resultSet.getInt("targetId")) {
final Target target = Target.initEmpty();
target.setId(resultSet.getInt("targetId"));
target.setText(resultSet.getString("targetText"));
// Create the treeitem for this target.
targetTreeItem = new TreeItem<>((Object) target);
targetTreeItem.setExpanded(false);
measureTreeItem.getChildren().add(targetTreeItem);
// Reset the children ids to ensure that they will be recreated.
lastTargetId = resultSet.getInt("targetId");
}
}
closeAll();
} catch (SQLException ex) {
super.errorMessage = ex.getMessage();
}
tree.setRoot(treeRoot);
final TargetTree targetTree = this;
tree.setCellFactory(new Callback<TreeView<Object>, TreeCell<Object>>() {
@Override
public TreeCell<Object> call(TreeView<Object> p) {
return new TargetTreeCell(targetTree);
}
});
// Select a Tree Item.
tree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() {
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue) {
selectedItem = (TreeItem) newValue;
}
});
}
public void collapseChildren() {
Thread thread = new Thread(new Task<Void>() {
@Override
protected Void call() throws Exception {
Platform.runLater(new Runnable() {
@Override
public void run() {
for (int i = 0; i < selectedItem.getChildren().size(); i++) {
TreeItem<Object> current = (TreeItem<Object>) selectedItem.getChildren().get(i);
if (!current.isLeaf()) {
current.setExpanded(false);
}
current = null;
}
selectedItem.setExpanded(false);
System.gc();
}
});
return null;
}
});
thread.setDaemon(true);
thread.start();
}
public void expandChildren() {
Thread thread = new Thread(new Task<Void>() {
@Override
protected Void call() throws Exception {
Platform.runLater(new Runnable() {
@Override
public void run() {
for (int i = 0; i < selectedItem.getChildren().size(); i++) {
TreeItem<Object> current = (TreeItem<Object>) selectedItem.getChildren().get(i);
if (!current.isLeaf()) {
current.setExpanded(true);
}
current = null;
}
selectedItem.setExpanded(true);
System.gc();
}
});
return null;
}
});
thread.setDaemon(true);
thread.start();
}
}
Below is the custom TreeCell class.
public class TargetTreeCell extends TreeCell<Object> {
private TargetTree targetTree;
public TargetTreeCell(TargetTree targetTree) {
super();
this.targetTree = targetTree;
}
@Override
public void updateItem(Object item, boolean empty) {
super.updateItem(item, empty);
if (item != null) {
if (item instanceof Target) {
initTarget(item);
} else if (item instanceof Measure) {
initMeasure(item);
} else if (item instanceof Section) {
initSection(item);
} else if (item instanceof Department) {
initDepartment(item);
} else if (item instanceof String) {
initRoot(item);
}
}
}
///<editor-fold defaultstate="collapsed" desc="Tree Item Initialization">
private void initRoot(Object item) {
// Create Menu Items.
MenuItem expandAllMenuItems = new MenuItem("Expand All");
MenuItem collapseAllMenuItems = new MenuItem("Collapse All");
// Event Haddlers for each Menu Items.
expandAllMenuItems.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
}
});
collapseAllMenuItems.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
targetTree.collapseChildren();
}
});
// Create Menu and add Menu Items.
ContextMenu contextMenu = new ContextMenu();
contextMenu.getItems().addAll(expandAllMenuItems, collapseAllMenuItems);
//Init Root Tree Item.
String root = (String) item;
setText(root);
setContextMenu(contextMenu);
}
private void initDepartment(Object item) {
// Create Menu Items.
MenuItem expandAllMenuItems = new MenuItem("Expand All");
MenuItem collapseAllMenuItems = new MenuItem("Collapse All");
// Event Haddlers for each Menu Items.
expandAllMenuItems.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
targetTree.expandChildren();
}
});
collapseAllMenuItems.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
targetTree.collapseChildren();
}
});
// Create Menu and add Menu Items.
ContextMenu contextMenu = new ContextMenu();
contextMenu.getItems().addAll(expandAllMenuItems, collapseAllMenuItems);
//Init Department Tree Item.
Department department = (Department) item;
setText(department.getName());
setContextMenu(contextMenu);
}
private void initSection(Object item) {
// Create Menu Items.
MenuItem expandAllMenuItems = new MenuItem("Expand All");
MenuItem collapseAllMenuItems = new MenuItem("Collapse All");
// Event Haddlers for each Menu Items.
expandAllMenuItems.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
targetTree.expandChildren();
}
});
collapseAllMenuItems.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
targetTree.collapseChildren();
}
});
// Create Menu and add Menu Items.
ContextMenu contextMenu = new ContextMenu();
contextMenu.getItems().addAll(expandAllMenuItems, collapseAllMenuItems);
//Init Section Tree Item.
Section section = (Section) item;
setText(section.getName());
setContextMenu(contextMenu);
}
private void initMeasure(Object item) {
// Create Menu Items.
MenuItem expandAllMenuItems = new MenuItem("Expand");
MenuItem collapseAllMenuItems = new MenuItem("Collapse");
// Event Haddlers for each Menu Items.
expandAllMenuItems.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
targetTree.expandChildren();
}
});
collapseAllMenuItems.setOnAction(new EventHandler() {
@Override
public void handle(Event event) {
targetTree.collapseChildren();
}
});
// Create Menu and add Menu Items.
ContextMenu contextMenu = new ContextMenu();
contextMenu.getItems().addAll(expandAllMenuItems, collapseAllMenuItems);
//Init Section Tree Item.
Measure measure = (Measure) item;
setText(measure.getLastname() + " " + measure.getFirstname());
setContextMenu(contextMenu);
}
private void initTarget(Object item) {
//Init Section Tree Item.
Target target = (Target) item;
setText(target.getText());
}
///</editor-fold>
}
If I have a copy-paste error please forgive me..I don't have problem with compiling. The code is running without errors. My problem is on the methods expandChildren() and collapseChildren() of the first class. In a previous version I didn't used threads and I used recursion to make all the children TreeItems (and their children TreeItems..) to collapse but the memory cost was more.