3

This might sound a bit complicated, I'll try to simplify what I am asking. A program I am developing can read and write from/to files using a JTextArea. When files are rather large, it does take a cumbersome amount of time to read the data from that file into the text area. As an example, I have a file that currently has 40,000 lines of text, roughly 50 characters a line; also, some lines wrap. There is quite a lot of text and it takes a lot more time to read from that file then I would like.

Currently, I am using the standard read method utilizing a BufferedReader instance that the JTextArea component includes. What I would like to do is load the JTextArea with a certain amount of text loaded on screen. The rest of the text that is off-screen loaded in a separate thread in the background.

Would using a InputStream and write each character to an array then write characters to the JTextArea be sufficient? Or should there be a different approach at this? I'm trying to accomplish a fast and efficient read method.

Paul Samsotha
  • 188,774
  • 31
  • 430
  • 651
Frizinator
  • 1,201
  • 2
  • 12
  • 16
  • (FWIW: A 2MB file isn't very "large" for today's hardware - that is, does it run too slow? For truly large files, then there are better methods.) – user2864740 Jul 30 '14 at 23:36
  • Well, it's actually 4.20 mb large, but I agree. However, it still does take longer to load than I like. – Frizinator Jul 30 '14 at 23:37
  • With a BufferedReader (wrap the InputStream) the sequential IO should be "almost instant". Time the loading (IO) and the loading (UI) and see which introduces the undesired delay. – user2864740 Jul 30 '14 at 23:39
  • Alright, I see. I am currently using a BufferedReader, with a wrapper FileReader. Would the InputStream be more efficient, then? – Frizinator Jul 30 '14 at 23:41
  • I would imagine [this would be the best way](http://stackoverflow.com/questions/4716503/best-way-to-read-a-text-file). – sgbj Jul 30 '14 at 23:50
  • 3
    Almost certainly the issue isn't in reading the file, it's in trying to have the JTextArea deal with the data. You could do some kind of manual pagination, only setting a certain subset of the data into the jtextarea. – Kylar Jul 30 '14 at 23:53
  • `JTable` can handle thousands of rows/lines, but people have a little trouble. – trashgod Jul 31 '14 at 00:18

1 Answers1

7

There are two, immediate, issues at hand

First, the need to read a file in such away that it can progressively update the UI without causing unacceptable delays

Second, the ability for the JTextArea to actually deal with this amount of data...

The first issues is, relatively, simple to fix. What you need to make sure of is that you are not blocking the Event Dispatching Thread while you read the file and that you are only updating the JTextArea from within the context of the Event Dispatching Thread. To this end a SwingWorker is an excellent choice, for example...

public class FileReaderWorker extends SwingWorker<List<String>, String> {

    private File file;
    private JTextArea ta;

    public FileReaderWorker(File file, JTextArea ta) {
        this.file = file;
        this.ta = ta;
    }

    public File getFile() {
        return file;
    }

    public JTextArea getTextArea() {
        return ta;
    }

    @Override
    protected List<String> doInBackground() throws Exception {
        List<String> contents = new ArrayList<>(256);
        try (BufferedReader br = new BufferedReader(new FileReader(getFile()))) {
            String text = null;
            while ((text = br.readLine()) != null) {
                // You will want to deal with adding back in the new line characters
                // here if that is important to you...
                contents.add(text);
                publish(text);
            }
        }
        return contents;
    }

    @Override
    protected void done() {
        try {
            get();
        } catch (InterruptedException | ExecutionException ex) {
            ex.printStackTrace();
            // Handle exception here...
        }
    }

    @Override
    protected void process(List<String> chunks) {
        JTextArea ta = getTextArea();
        for (String text : chunks) {
            ta.append(text);
        }
    }

}

Take a look at Concurrency in Swing and Worker Threads and SwingWorker for more details

ps- You don't need to use the List to store the contents, I just did it as an example...

The second problem is far more complicated and would need some additional testing to ensure that it is actually a problem, but generally speaking, contents of over about 1mb tends to lead to issues...

To this end, you would need to be able to manage the JScrollPane, be able to request chunks of text from the file both in backwards and forwards direction and try and effectively "fudge" the process (so that you only have the text you need loaded, but can still make it look like you have all the text loaded within the JTextArea)...

You could also take a look at FileChannel, which provides more functionality over the standard java.io classes, including memory mapping, for starters, have a look at Reading, Writing, and Creating Files.

You also might consider using a JList or JTable which are highly optimised for displaying large quantities of data. There are limitations to this, as there is an expectation of fixed row heights, which when changed (to dynamic row heights) can affect the performance, but might be a suitable alternative...

MadProgrammer
  • 323,026
  • 21
  • 204
  • 329
  • 1
    I'd try `JList` or `JTable` for the view in order to leverage the flyweight rendering. – trashgod Jul 31 '14 at 00:21
  • 1
    @trashgod For viewing, absolutely, if the OP is expecting the ability to edit the values, it becomes a little more complicated, not impossible though.... – MadProgrammer Jul 31 '14 at 00:23
  • Good point; the default `JTable` editor would be sufficient, but write-back could be tedious. – trashgod Jul 31 '14 at 00:26
  • There's also issues of fixed and dynamic row heights, with fixed row heights the components are very well optimised, dynamic row heights means that the components need to test each row to be able to calculate the height of the component, if done carefully, updates can be done well, but it's a consideration to keep in mind... – MadProgrammer Jul 31 '14 at 00:28