然而,所提出的解决方案似乎都没有涵盖“简单”应用程序的情况:我有一棵大树(即一棵非常深、非常宽或两者兼而有之的树),我希望以编程方式完全扩展它。
下面是一个显示问题的MCVE:它创建了一个有100k个节点的树模型。按下该按钮会触发对expandAll
的调用,该调用尝试使用从相关问题的答案派生的方法展开所有节点。
问题是,扩展这100k个节点大约需要13秒(在一台普通机器上,使用最新的JVM)。
import java.awt.BorderLayout;
import java.awt.GridLayout;
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.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
public class TreeExpansionPerformanceProblem
{
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(
TreeExpansionPerformanceProblem::expandAll));
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 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);
}
}
}
}
当完全展开一棵大树时,会遇到各种瓶颈,以及不同的方法来规避这些瓶颈。
有趣的是,收集treepath
对象进行扩展并遍历树并不是开销最大的部分。根据在VisualVM和Java Flight Recorder中运行的profiler,大部分时间都花在计算模型状态(TreeModel
)和视图(JTree
)之间的“映射”上。这主要是指
JTree
TreeCellRenderer中标签的界限
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);
}
}
}
}
备注:
>
这是一个自我回答的问题,我希望这个答案可能对其他人有所帮助。尽管如此,1秒还是相当慢的。我还尝试了一些其他方法,例如设置tree.setlargeModel(true)
,但这并没有产生积极的效果(事实上,它甚至更慢!)。大部分时间仍然花在树的视觉状态的最后更新上,我很乐意看到这里的进一步改进。
ExpandallRecursively
方法可以由涉及DefaultMutableTreenode#BreadthFirstEnumeration
和DefaultTreemodel#GetPathtoroot
的几行替换,而不会牺牲太多性能。但是在当前的形式中,代码只在TreeModel
接口上操作,并且应该使用任何类型的节点。
我正在跟踪粒子到三维晶格中。每个晶格元素都有一个对应于展开的3D数组的索引 我对从S1单元到S2单元的过渡感兴趣。由此产生的过渡矩阵M(S1,S2)人口稀少,因为粒子只能在细胞附近到达。不幸的是,使用几何上接近的展开3D阵列单元的索引可能会在索引上有很大差异。例如,位于彼此顶部(例如z和z 1处)的单元格的索引将按宽度*深度移动。因此,如果我尝试累积得到的2D矩阵M(S1,S2),S1和S2将非常
我正在尝试下载大文件( 这是因为它在不使用太多内存的情况下下载文件,但它似乎不必要地无效,因为它不断尝试写入更多数据,而不知道是否有任何新数据到达。这似乎也通过我自己的测试得到了证实,同时在资源非常有限的VM上运行它,因为它似乎使用了更多的CPU,同时下载速度低于python中的类似脚本,并且使用是有原因的。 我想知道是否有一种方法可以让某些东西回调,如果x字节可用,或者它是文件的末尾,那么我就可
问题内容: 我有一个150MB的单张excel文件,使用以下命令在功能非常强大的计算机上打开大约需要7分钟: 有什么办法可以更快地打开excel文件吗?我愿意接受甚至非常古怪的建议(例如hadoop,spark,c,java等)。理想情况下,如果这不是白日梦,我正在寻找一种在30秒内打开文件的方法。另外,上面的示例使用的是python,但不一定必须是python。 注意:这是来自客户端的Excel
问题内容: 我有非常大的XML文件要处理。我想将它们转换为具有颜色,边框,图像,表格和字体的可读PDF。我的机器上没有很多资源,因此,我需要我的应用程序对内存和处理器的寻址非常理想。 我进行了不起眼的研究,以使自己对所使用的技术有所了解,但是我无法确定什么是满足我的要求的最佳编程语言和API。我认为DOM不是一个选择,因为它会占用大量内存,但是带SAX解析器的Java是否可以满足我的要求? 有人还
我正在处理一个非常宽的数据集(1005行*590,718列,1.2g)。将如此大的数据集加载到pandas dataframe中会导致完全由于内存不足而导致代码失败。 我知道Spark可能是处理大型数据集的Pandas的一个很好的替代方案,但是Pandas中是否有任何适合的解决方案来减少加载大型数据时的内存占用?
我有INT数组 我也在寻找类似的问题,但我只发现了java.util.stream.stream .sorted()的大O复杂性,这一点也没有帮助,因为有两个不同的答案(第一个当然是部分错误的,因为arrays.sort并不总是O(n log n))。第二个呢?我还没找到证据。