Java 如何有效地充分扩展大型JTree
有几个相关的问题,关于,或者关于,其中一些问题也是针对 然而,没有一个建议的解决方案似乎涵盖了一个简单的应用案例:我有一棵大树(也就是一棵树,它要么非常深,要么非常宽,要么两者都是),我想以编程方式充分地扩展它。p> 下面是一个说明问题的示例:它创建了一个具有100k个节点的树模型。按下按钮将触发对Java 如何有效地充分扩展大型JTree,java,performance,swing,jtree,Java,Performance,Swing,Jtree,有几个相关的问题,关于,或者关于,其中一些问题也是针对 然而,没有一个建议的解决方案似乎涵盖了一个简单的应用案例:我有一棵大树(也就是一棵树,它要么非常深,要么非常宽,要么两者都是),我想以编程方式充分地扩展它。p> 下面是一个说明问题的示例:它创建了一个具有100k个节点的树模型。按下按钮将触发对expandAll的调用,该调用尝试使用从相关问题的答案中派生的方法展开所有节点 问题是,扩展这些100k节点大约需要13秒(在一台普通机器上,使用最新的JVM) 导入java.awt.BorderL
expandAll
的调用,该调用尝试使用从相关问题的答案中派生的方法展开所有节点
问题是,扩展这些100k节点大约需要13秒(在一台普通机器上,使用最新的JVM)
导入java.awt.BorderLayout;
导入java.awt.GridLayout;
导入java.util.function.Consumer;
导入javax.swing.JButton;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.JScrollPane;
导入javax.swing.JTree;
导入javax.swing.SwingUtilities;
导入javax.swing.tree.DefaultMutableTreeNode;
导入javax.swing.tree.DefaultTreeModel;
导入javax.swing.tree.TreeModel;
公共类树扩展性能问题
{
公共静态void main(字符串[]args)
{
SwingUtilities.invokeLater(
()->createAndShowGUI());
}
私有静态void createAndShowGUI()
{
JFrame f=新的JFrame();
f、 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f、 getContentPane().setLayout(新的GridLayout(1,0));
f、 getContentPane().add(createTestPanel(
TreeExpansionPerformanceProblem::expandAll);
f、 设置大小(800600);
f、 setLocationRelativeTo(空);
f、 setVisible(真);
}
私有静态JPanel createTestPanel(使用者扩展方法)
{
JPanel panel=newjpanel(newborderlayout());
JTree树=新的JTree(createTestTreeModel());
添加(新的JScrollPane(树),BorderLayout.CENTER);
JPanel buttonPanel=新的JPanel();
JButton expandAllButton=新JButton(“全部展开”);
expandAllButton.addActionListener(e->
{
System.out.println(“扩展…”);
很久以前=System.nanoTime();
expansionMethod.accept(树);
long after=System.nanoTime();
System.out.println(“扩展带”+(在前)/1e6);
});
buttonPanel.add(expandAllButton);
面板。添加(按钮面板,边界布局。南);
返回面板;
}
私有静态void expandAll(JTree树)
{
int r=0;
而(r 对于(inti=0;i而言,当完全扩展一棵大树时,会有各种瓶颈,并且有不同的方法可以绕过这些瓶颈
有趣的是,为扩展收集TreePath
对象并遍历树通常不是最昂贵的部分。根据和中运行的探查器,大部分时间都花在计算模型状态(TreeModel
)和视图(JTree)之间的“映射”上。这主要是指
- 计算
JTree的行高
- 计算
TreeCellRenderer
- 使用
JTree\setRowHeight
- 暂时禁用树的
treeeexpansionlisteners
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeExpansionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
public class TreeExpansionPerformanceSolution
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(
() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new GridLayout(1,0));
f.getContentPane().add(createTestPanel(
TreeExpansionPerformanceSolution::expandAll));
f.getContentPane().add(createTestPanel(
TreeExpansionPerformanceSolution::expandAllFaster));
f.setSize(800,600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static JPanel createTestPanel(Consumer<JTree> expansionMethod)
{
JPanel panel = new JPanel(new BorderLayout());
JTree tree = new JTree(createTestTreeModel());
panel.add(new JScrollPane(tree), BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
JButton expandAllButton = new JButton("Expand all");
expandAllButton.addActionListener( e ->
{
System.out.println("Expanding...");
long before = System.nanoTime();
expansionMethod.accept(tree);
long after = System.nanoTime();
System.out.println("Expanding took "+(after-before)/1e6);
});
buttonPanel.add(expandAllButton);
panel.add(buttonPanel, BorderLayout.SOUTH);
return panel;
}
private static void expandAll(JTree tree)
{
int r = 0;
while (r < tree.getRowCount())
{
tree.expandRow(r);
r++;
}
}
private static void expandAllFaster(JTree tree)
{
// Determine a suitable row height for the tree, based on the
// size of the component that is used for rendering the root
TreeCellRenderer cellRenderer = tree.getCellRenderer();
Component treeCellRendererComponent =
cellRenderer.getTreeCellRendererComponent(
tree, tree.getModel().getRoot(), false, false, false, 1, false);
int rowHeight = treeCellRendererComponent.getPreferredSize().height + 2;
tree.setRowHeight(rowHeight);
// Temporarily remove all listeners that would otherwise
// be flooded with TreeExpansionEvents
List<TreeExpansionListener> expansionListeners =
Arrays.asList(tree.getTreeExpansionListeners());
for (TreeExpansionListener expansionListener : expansionListeners)
{
tree.removeTreeExpansionListener(expansionListener);
}
// Recursively expand all nodes of the tree
TreePath rootPath = new TreePath(tree.getModel().getRoot());
expandAllRecursively(tree, rootPath);
// Restore the listeners that the tree originally had
for (TreeExpansionListener expansionListener : expansionListeners)
{
tree.addTreeExpansionListener(expansionListener);
}
// Trigger an update for the TreeExpansionListeners
tree.collapsePath(rootPath);
tree.expandPath(rootPath);
}
// Recursively expand the given path and its child paths in the given tree
private static void expandAllRecursively(JTree tree, TreePath treePath)
{
TreeModel model = tree.getModel();
Object lastPathComponent = treePath.getLastPathComponent();
int childCount = model.getChildCount(lastPathComponent);
if (childCount == 0)
{
return;
}
tree.expandPath(treePath);
for (int i=0; i<childCount; i++)
{
Object child = model.getChild(lastPathComponent, i);
int grandChildCount = model.getChildCount(child);
if (grandChildCount > 0)
{
class LocalTreePath extends TreePath
{
private static final long serialVersionUID = 0;
public LocalTreePath(
TreePath parent, Object lastPathComponent)
{
super(parent, lastPathComponent);
}
}
TreePath nextTreePath = new LocalTreePath(treePath, child);
expandAllRecursively(tree, nextTreePath);
}
}
}
private static TreeModel createTestTreeModel()
{
DefaultMutableTreeNode root = new DefaultMutableTreeNode("JTree");
addNodes(root, 0, 6, 6, 10);
return new DefaultTreeModel(root);
}
private static void addNodes(DefaultMutableTreeNode node,
int depth, int maxDepth, int count, int leafCount)
{
if (depth == maxDepth)
{
return;
}
for (int i=0; i<leafCount; i++)
{
DefaultMutableTreeNode leaf =
new DefaultMutableTreeNode("depth_"+depth+"_leaf_"+i);
node.add(leaf);
}
if (depth < maxDepth - 1)
{
for (int i=0; i<count; i++)
{
DefaultMutableTreeNode child =
new DefaultMutableTreeNode("depth_"+depth+"_node_"+i);
node.add(child);
addNodes(child, depth+1, maxDepth, count, leafCount);
}
}
}
}
导入java.awt.BorderLayout;
导入java.awt.Component;
导入java.awt.GridLayout;
导入java.util.array;
导入java.util.List;
导入java.util.function.Consumer;
导入javax.swing.JButton;
导入javax.swing.JFrame;
导入javax.swing.JPanel;
导入javax.swing.JScrollPane;
导入javax.swing.JTree;
导入javax.swing.SwingUtilities;
导入javax.swing.event.TreeExpansionListener;
导入javax.swing.tree.DefaultMutableTreeNode;
导入javax.swing.tree.DefaultTreeModel;
导入javax.swing.tree.TreeCellRenderer;
导入javax.swing.tree.TreeModel;
导入javax.swing.tree.TreePath;
公共类树扩展性能解决方案
{
公共静态void main(字符串[]args)
{
SwingUtilities.invokeLater(
()->createAndShowGUI());
}
私有静态void createAndShowGUI()
{
JFrame f=新的JFrame();
f、 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f、 getContentPane().setLayout(新的GridLayout(1,0));
f、 getContentPane().add(createTestPanel(
TreeExpansionPerformanceSolution::expandAll);
f、 getContentPane().add(createTestPanel(
TreeExpansionPerformanceSolution::expandAllFaster);
f、 设置大小(800600);
f、 setLocationRelativeTo(空);
f、 setVisible(真);
}
私有静态JPanel createTestPanel(使用者扩展方法)
{
JPanel panel=newjpanel(newborderlayout());
JTree树=新的JTree(createTestTreeModel());
添加(新的JScrollPane(树),BorderLayout.CENTER);
JPanel buttonPanel=新的JPanel();
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.event.TreeExpansionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
public class TreeExpansionPerformanceSolution
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(
() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new GridLayout(1,0));
f.getContentPane().add(createTestPanel(
TreeExpansionPerformanceSolution::expandAll));
f.getContentPane().add(createTestPanel(
TreeExpansionPerformanceSolution::expandAllFaster));
f.setSize(800,600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
private static JPanel createTestPanel(Consumer<JTree> expansionMethod)
{
JPanel panel = new JPanel(new BorderLayout());
JTree tree = new JTree(createTestTreeModel());
panel.add(new JScrollPane(tree), BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
JButton expandAllButton = new JButton("Expand all");
expandAllButton.addActionListener( e ->
{
System.out.println("Expanding...");
long before = System.nanoTime();
expansionMethod.accept(tree);
long after = System.nanoTime();
System.out.println("Expanding took "+(after-before)/1e6);
});
buttonPanel.add(expandAllButton);
panel.add(buttonPanel, BorderLayout.SOUTH);
return panel;
}
private static void expandAll(JTree tree)
{
int r = 0;
while (r < tree.getRowCount())
{
tree.expandRow(r);
r++;
}
}
private static void expandAllFaster(JTree tree)
{
// Determine a suitable row height for the tree, based on the
// size of the component that is used for rendering the root
TreeCellRenderer cellRenderer = tree.getCellRenderer();
Component treeCellRendererComponent =
cellRenderer.getTreeCellRendererComponent(
tree, tree.getModel().getRoot(), false, false, false, 1, false);
int rowHeight = treeCellRendererComponent.getPreferredSize().height + 2;
tree.setRowHeight(rowHeight);
// Temporarily remove all listeners that would otherwise
// be flooded with TreeExpansionEvents
List<TreeExpansionListener> expansionListeners =
Arrays.asList(tree.getTreeExpansionListeners());
for (TreeExpansionListener expansionListener : expansionListeners)
{
tree.removeTreeExpansionListener(expansionListener);
}
// Recursively expand all nodes of the tree
TreePath rootPath = new TreePath(tree.getModel().getRoot());
expandAllRecursively(tree, rootPath);
// Restore the listeners that the tree originally had
for (TreeExpansionListener expansionListener : expansionListeners)
{
tree.addTreeExpansionListener(expansionListener);
}
// Trigger an update for the TreeExpansionListeners
tree.collapsePath(rootPath);
tree.expandPath(rootPath);
}
// Recursively expand the given path and its child paths in the given tree
private static void expandAllRecursively(JTree tree, TreePath treePath)
{
TreeModel model = tree.getModel();
Object lastPathComponent = treePath.getLastPathComponent();
int childCount = model.getChildCount(lastPathComponent);
if (childCount == 0)
{
return;
}
tree.expandPath(treePath);
for (int i=0; i<childCount; i++)
{
Object child = model.getChild(lastPathComponent, i);
int grandChildCount = model.getChildCount(child);
if (grandChildCount > 0)
{
class LocalTreePath extends TreePath
{
private static final long serialVersionUID = 0;
public LocalTreePath(
TreePath parent, Object lastPathComponent)
{
super(parent, lastPathComponent);
}
}
TreePath nextTreePath = new LocalTreePath(treePath, child);
expandAllRecursively(tree, nextTreePath);
}
}
}
private static TreeModel createTestTreeModel()
{
DefaultMutableTreeNode root = new DefaultMutableTreeNode("JTree");
addNodes(root, 0, 6, 6, 10);
return new DefaultTreeModel(root);
}
private static void addNodes(DefaultMutableTreeNode node,
int depth, int maxDepth, int count, int leafCount)
{
if (depth == maxDepth)
{
return;
}
for (int i=0; i<leafCount; i++)
{
DefaultMutableTreeNode leaf =
new DefaultMutableTreeNode("depth_"+depth+"_leaf_"+i);
node.add(leaf);
}
if (depth < maxDepth - 1)
{
for (int i=0; i<count; i++)
{
DefaultMutableTreeNode child =
new DefaultMutableTreeNode("depth_"+depth+"_node_"+i);
node.add(child);
addNodes(child, depth+1, maxDepth, count, leafCount);
}
}
}
}