长弦摇摆很慢

问题描述 投票:0回答:2

我构建了一个简单的 Java 程序,用于登录 JTextArea 组件。

JTextArea _log = new JTextArea();
_log.setEditable(false);
JScrollPane scrollLog = new JScrollPane(_log);
scrollLog.setPreferredSize(getMaximumSize());
add(scrollLog);

问题是这样的日志记录平均需要15ms

public void log(String info) {
    _log.append(info + "\n");
}

这比使用

System.out.println
记录要慢得多(!)。记录日志花费的时间比算法的整个运行时间还要多!

为什么 JTextArea 这么慢?有办法改善吗?

编辑1:

我对算法使用单独的线程,并使用

SwingUtilities.invokeLater
更新 UI 中的日志。

算法平均在 130 毫秒后完成工作,但 JTextArea 平均在 6000 毫秒后完成

append

编辑2:

我尝试使用包含 2500 个字符的字符串

setText
来测试这一点。在这种情况下,操作平均需要 1000 毫秒。 我尝试使用另一个控制器,然后是 JTextArea,我得到了相同的结果。

Swing 组件处理大字符串是否困难?我能做什么呢?

编辑3:

我只是用这段代码进行测试:

public class Test extends JFrame {

    public Test() {
        final JTextArea log = new JTextArea();
        log.setEditable(false);
        log.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
        JScrollPane scrollLog = new JScrollPane(log);
        scrollLog.setPreferredSize(getMaximumSize());

        JButton start = new JButton("Start");
        start.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {

                long start = System.nanoTime();
                for (int i = 0; i < 2500; i++) {
                    log.append("a\n");
                }
                long end = System.nanoTime();
                System.out.println((end - start) / 1000000.0);
            }
        });

        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(2, 1));
        panel.add(scrollLog);
        panel.add(start);
        add(panel);
    }

    public static void main(String[] args) {
         Test frame = new Test();
         frame.setSize(600,500);
         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
         frame.setVisible(true);
    }
}

for 循环的平均时间为 1870 毫秒。

这是我运行的唯一代码(包括问题顶部的

_log
声明)

java swing
2个回答
3
投票

JTextArea 并不慢。

远离 System.out.println。

System.out.println() 在单独的线程上执行。

日志花费的时间比算法的洞运行时间还要多!

因此,您的算法可能在

Event Dispatch Thread (EDT)
上执行,该线程与将文本附加到文本区域的逻辑相同。因此,在算法完成之前,文本区域无法重新绘制自身。

解决方案是为长时间运行的算法使用单独的线程。

或者也许更好的选择是使用

SwingWorker
,这样您就可以运行算法并将结果“发布”到文本区域。

阅读 Swing 教程中关于 Concurrency 的部分,了解更多信息和

SwingWorker
的工作示例。

编辑:

//log.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);

上面的行导致了问题。第一次测试我得到 125,当我继续点击按钮时得到 45。

不需要该属性。文本仍显示在文本窗格的左侧。如果您想要右对齐文本,那么您需要使用

JTextPane
并将文本窗格的属性设置为右对齐。

这就是为什么您应该始终发布

MCVE
。我们不可能从您最初的问题中猜出您正在使用该方法。

编辑2:

使用 JTextPane 的对齐功能:

SimpleAttributeSet center = new SimpleAttributeSet();
StyleConstants.setAlignment(center, StyleConstants.ALIGN_CENTER);
textPane.getStyledDocument().setParagraphAttributes(0, doc.getLength(), center, false);

现在添加到文档中的任何新文本都应该居中对齐。您可以将其更改为右侧。


0
投票

JTextArea
难以置信地慢,并且与您从哪个线程调用它无关。如果你对其进行采样,它会花费所有时间在 BoxView.layout()、GlyphView.getMinimumSpan()、TextMeasurer.calcLineBreak()、Strike.getGlyphOutline() 等方法中,所有这些方法都是从 getPreferredSize() 调用的,尽管它在第一次计算后最少缓存布局信息。

我刚刚做了一个快速测试,在只读 JTextArea 中显示相对较小的日志文件(1.8 MB,14500 行)在布局和首次出现之前大约需要 21.5 秒。

要获得更快更快的方法,请尝试使用 JList

 代替。扫描您显示的文本,找出换行符应该在哪里(在换行符上或在您达到所需的行长度时,具体取决于您是想要长行还是将其换行到窗口宽度)。显然,这假设您使用的是等宽字体,但您可能希望日志查看器能够使用这种字体。

但是如果您使用 JList 而不是 JTextArea,速度的提高是令人眼花缭乱的。从 21.5 秒缩短至 70 毫秒,速度提高约 250 倍!

缺点是,如果您希望能够从日志查看器中选择和复制文本(我还没有做到这一点!),那么它需要更多的工作,但如果您只需要显示大量文本,那么 JList 是无限的比 JTextArea 更快。

© www.soinside.com 2019 - 2024. All rights reserved.