Loading and displaying large text files

后端 未结 3 1490
一生所求
一生所求 2020-11-22 03:14

In a Swing application, I sometimes need to support read-only access to large, line-oriented text files that are slow to load: logs, dumps, traces, etc. For small amounts of

3条回答
  •  礼貌的吻别
    2020-11-22 03:39

    Because of the size, you'll surely want to load the file in the background to avoid blocking the event dispatch thread; SwingWorker is a common choice. Instead of using a Document, consider updating a TableModel and displaying the lines of text in the rows of a JTable. This offers several advantages:

    • Results will begin appearing immediately, and there will be reduced perceived latency.

    • JTable uses the flyweight pattern for rendering, which scales well into the multi-megabyte, million-line range.

    • You can parse the input as it is being read to create an arbitrary column structure.

    • You can leverage the sorting and filtering features of JTable, for example.

    • You can use TablePopupEditor to focus on a single line.

    Addendum: The example below uses DefaultTableModel for convenience. To reduce overhead, extend AbstractTableModel and manage a List or List, as shown here. The example displays indeterminate progress; changes to display intermediate progress are shown here.

    Code:

    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import java.beans.PropertyChangeEvent;
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    import java.util.List;
    import javax.swing.JFrame;
    import javax.swing.JProgressBar;
    import javax.swing.JScrollPane;
    import javax.swing.JTable;
    import javax.swing.SwingWorker;
    import javax.swing.table.DefaultTableModel;
    import javax.swing.table.TableModel;
    
    /**
     * @see https://stackoverflow.com/a/25526869/230513
     */
    public class DisplayLog {
    
        private static final String NAME = "/var/log/install.log";
    
        private static class LogWorker extends SwingWorker {
    
            private final File file;
            private final DefaultTableModel model;
    
            private LogWorker(File file, DefaultTableModel model) {
                this.file = file;
                this.model = model;
                model.setColumnIdentifiers(new Object[]{file.getAbsolutePath()});
            }
    
            @Override
            protected TableModel doInBackground() throws Exception {
                BufferedReader br = new BufferedReader(new FileReader(file));
                String s;
                while ((s = br.readLine()) != null) {
                    publish(s);
                }
                return model;
            }
    
            @Override
            protected void process(List chunks) {
                for (String s : chunks) {
                    model.addRow(new Object[]{s});
                }
            }
        }
    
        private void display() {
            JFrame f = new JFrame("DisplayLog");
            f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            DefaultTableModel model = new DefaultTableModel();
            JTable table = new JTable(model);
            JProgressBar jpb = new JProgressBar();
            f.add(jpb, BorderLayout.NORTH);
            f.add(new JScrollPane(table));
            f.pack();
            f.setLocationRelativeTo(null);
            f.setVisible(true);
            LogWorker lw = new LogWorker(new File(NAME), model);
            lw.addPropertyChangeListener((PropertyChangeEvent e) -> {
                SwingWorker.StateValue s = (SwingWorker.StateValue) e.getNewValue();
                jpb.setIndeterminate(s.equals(SwingWorker.StateValue.STARTED));
            });
            lw.execute();
        }
    
        public static void main(String[] args) {
            EventQueue.invokeLater(() -> {
                new DisplayLog().display();
            });
        }
    }
    

提交回复
热议问题