0

I'm developing an application that has a File Tree to the side of the main window and it works fine for folders that don't contain a lot of files, but it gets a bit slow otherwise. Here's the model code:

public class FileTreeModel implements TreeModel {
private File root;
private List<TreeModelListener> listeners;

public FileTreeModel(File root) {
    this.root = root;
    this.listeners = new ArrayList<TreeModelListener>();
}

@Override
public void addTreeModelListener(TreeModelListener listener) {
    this.listeners.add(listener);
}

@Override
public Object getChild(Object parent, int index) {
    File directory = (File) parent;
    String[] children = getSortedChildren(directory);
    return new File(directory, children[index]) {
        private static final long serialVersionUID = 1L;

        @Override
        public String toString() { // This is so the node only shows the file name instead of the whole path
            return getName();
        }
    };
}

@Override
public int getChildCount(Object parent) {
    File file = (File) parent;
    if (file.isDirectory()) {
        String[] fileList = file.list();
        if (fileList != null) {
            return file.list().length;
        }
    }
    return 0;
}

@Override
public int getIndexOfChild(Object parent, Object child) {
    File directory = (File) parent;
    File file = (File) child;
    String[] children = getSortedChildren(directory);
    for (int i = 0; i < children.length; i++) {
        if (file.getName().equals(children[i])) {
            return i;
        }
    }
    return -1;
}

@Override
public Object getRoot() {
    return this.root;
}

@Override
public boolean isLeaf(Object node) {
    File file = (File) node;
    return file.isFile();
}

@Override
public void removeTreeModelListener(TreeModelListener listener) {
    this.listeners.remove(listener);
}

@Override
public void valueForPathChanged(TreePath path, Object value) {
    File oldFile = (File) path.getLastPathComponent();
    String fileParentPath = oldFile.getParent();
    String newFileName = (String) value;
    File targetFile = new File(fileParentPath, newFileName);
    oldFile.renameTo(targetFile);
    File parent = new File(fileParentPath);
    int[] changedChildrenIndices = { getIndexOfChild(parent, targetFile) };
    Object[] changedChildren = { targetFile };
    fireTreeNodesChanged(path.getParentPath(), changedChildrenIndices, changedChildren);
}

private void fireTreeNodesChanged(TreePath parentPath, int[] indices, Object[] children) {
    TreeModelEvent event = new TreeModelEvent(this, parentPath, indices, children);
    for (TreeModelListener listener : this.listeners) {
        listener.treeNodesChanged(event);
    }
}

private String[] getSortedChildren(File directory) {
    File[] directoryChildren = directory.listFiles(new FileFilter() {
        @Override
        public boolean accept(File pathname) {
            return pathname.isDirectory();
        }
    });
    Arrays.sort(directoryChildren);
    File[] fileChildren = directory.listFiles(new FileFilter() {
        @Override
        public boolean accept(File pathname) {
            return pathname.isFile();
        }
    });
    Arrays.sort(fileChildren);
    int arraySize = directoryChildren.length + fileChildren.length;
    String[] sortedChildren = new String[arraySize];
    for (int i = 0; i < directoryChildren.length; i++) {
        sortedChildren[i] = directoryChildren[i].getName();
    }
    for (int i = directoryChildren.length; i < arraySize; i++) {
        sortedChildren[i] = fileChildren[i - directoryChildren.length].getName();
    }
    return sortedChildren;
}
}

I suspect the bottleneck is in the getSortedChildren method, and I need this method to give me the contents of a directory, the folders first, sorted alphabetically, and the files next, also sorted alphabetically.

Is there any way to optimize my code for directories with lots of files?

Thanks

user2711115
  • 457
  • 3
  • 18
  • Why not create a `List` of custom objects that know if they're a file or directory and whose `toString` method returns their name. Make those objects sortable and then use `Collections.sort`. Your method is a little...obtuse. – Boris the Spider Jun 28 '14 at 10:25
  • 1
    But I don't think that is the issue. I think this issue is all your other methods which call the `getSortedChildren` method **each time they are called**, for example `getChild` and `getIndexOfChild`. I think you need to optimise your code to cache values currently being displayed. – Boris the Spider Jun 28 '14 at 10:27
  • Suspect? [Profile](http://stackoverflow.com/q/2064427/230513). – trashgod Jun 28 '14 at 10:32
  • Been profiling using jvisualvm, here are the results: http://s4.postimg.org/dj3yxhlgt/results.png getSortedChildren indeed seems to be the problem, its being called way too many times as I've only browsed about 10 nodes and its already been called 1219 times. ill try caching as suggested – user2711115 Jun 28 '14 at 10:43
  • You will need to cache anyway for a different reason: you can currently get data inconsistency problems. What happens if files are added to or deleted from a directory while the `JTree` is open? Then you may get `IndexOutOfBoundsExceptions` when files are added, you may end up pointing to the wrong file when their their positions in the sorted list changes, etc. – Erwin Bolwidt Jun 28 '14 at 11:09
  • Okay, using a simple Map to store the contents of the node that's being called getSortedChildren on drastically improved performance, please post an answer so I can accept it :) – user2711115 Jun 28 '14 at 11:10

0 Answers0