我正在创建一个记录器,以显示输出作为更大的Java swing GUI的一部分。不幸的是,我添加它后经历了一个减速。我已经将问题追溯到Document.insertString()
的重复调用。
我做了一个测试,显示了这种放缓:
LogPanel.java
public class LogPanel extends JPanel{
private static final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private JEditorPane textPane;
private static int numTextRows = 50;
private SimpleAttributeSet keyWord;
private Document document;
public LogPanel() {
super(new BorderLayout());
add(makePanel(), BorderLayout.CENTER);
}
private Component makePanel() {
// Just a text area that grows and can be scrolled.
textPane = new JTextPane();
document = textPane.getDocument();
keyWord = new SimpleAttributeSet();
StyleConstants.setForeground(keyWord, Color.BLACK);
//textArea.setRows(numTextRows);
textPane.setEditable(false);
textPane.setFont(new Font("monospaced", Font.PLAIN, 12));
DefaultCaret caret = (DefaultCaret) textPane.getCaret();
caret.setUpdatePolicy(DefaultCaret.ALWAYS_UPDATE);
//Wrap the textPane in a JPanel with BorderLayout so that the text does not wrap
JPanel textPaneWrapper = new JPanel();
textPaneWrapper.setLayout(new BorderLayout());
textPaneWrapper.add(textPane);
JScrollPane areaScrollPane = new JScrollPane(textPaneWrapper);
areaScrollPane.getVerticalScrollBar().setUnitIncrement(20);
//JScrollPane areaScrollPane = new JScrollPane(textPane);
areaScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
areaScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
areaScrollPane.setPreferredSize(new Dimension(250, 250));
// builder.add(areaScrollPane, cc.xywh(1, 3, 3, 1));
StyleConstants.setBackground(keyWord, areaScrollPane.getBackground());
textPane.setBackground(areaScrollPane.getBackground());
return areaScrollPane;
}
public void appendResult(final String action, final String result, final Color color) {
Date now = new Date();
String strDate = df.format(now);
String paddedAction = String.format("%-19s", action);
StyleConstants.setForeground(keyWord, color);
try {
document.insertString(document.getLength(), strDate + " " + paddedAction + " " + result + "\n", keyWord);
} catch (BadLocationException e) {
throw new RuntimeException(e);
}
if(!textPane.hasFocus()) {
textPane.setCaretPosition(document.getLength());
}
}
public void appendResult(String action, String result) {
appendResult(action, result, Color.BLACK);
}
}
LogTester.Java
public class LogTester extends JFrame{
private LogPanel logPanel;
private JButton pushMe;
public LogTester(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridBagLayout());
logPanel = new LogPanel();
this.add(logPanel);
pushMe= new JButton("Press Me");
LogTester self=this;
pushMe.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
self.addLotsOfStuff();
}
});
this.add(pushMe);
this.pack();
this.setVisible(true);
}
public void addLotsOfStuff(){
for(int i=0; i<1000; i+=1){
long start=System.currentTimeMillis();
for(int j=0; j<1000; j+=1){
String str="This is a very long peice of text designed to test the capabilites of our log panel. Move along, nothing to see here.";
logPanel.appendResult("HERP",str);
}
long end=System.currentTimeMillis();
System.out.println(end-start);
}
}
public static void main(String args[]){
LogTester test=new LogTester();
}
}
上述程序尝试将大量行写入JTextPane
,使用Document.insertString()
。该计划的结果涉及:
由于某种原因,每次调用此函数都会增加下一次调用的运行时间:它看起来是线性的,甚至是温和的指数。这可能意味着文档的所有先前内容都被复制到每个插入内容上,而不是附加的新字符串(以某种链表方式)
与Java GUI freezing because of insertString method?不同,我主要关注的是函数运行时间的增加,而不是应用程序的空闲时间。如果每个单独的呼叫变得非常慢,则添加线程将无济于事。
与Limit JTextPane space usage不同,我不关心大量内存使用情况。如果文档很大,它将使用大量内存。我只是不明白为什么会影响向Document插入更多信息的运行时。
也许减速可以归结为caret position memory leak?
为了实现恒定时间insertString()
,我必须覆盖JTextPane或Document的哪些部分?
答案 0 :(得分:3)
增长数组几乎是线性的,但实际上不是线性的。
一旦您超过了1级缓存行的大小,就使用另一个,直到:
这意味着,即使对于线性运算,很小的东西也比处理大量数据的线性算法运行得快得多。
因此,在确定算法是否为O(n)时,请务必记住,这样的决定是基于对计算模型期望值的基本理解。 Big-O表示法假设所有RAM提取在时间上均相等,并且计算(操作)在时间上也均相等。在这种约束下,将操作计数与数据量进行比较仍然是有意义的。但是,假设墙上时钟的时间不正确。