Search files from large directory and sub-directories Essentially I have a large directory with many sub-folders and different formats of files.  It reads through XML, JAVA, .txt, etc, in search of a selected String inputted by the user.  The results come back as snippets, reading a certain amount of characters previous and after the selected string has been found. 

It does function as planned...I send the results as snippets and links to the main pages through REST calls via Spring and AJAX.  It does take about 5 - 7 seconds on a localhost server to return the results for a directory that is 19,405 Files, 9,610 Folders.

private ArrayList&lt;String&gt; pathNames = new ArrayList();
private HashMap&lt;String, String&gt; snippets = new HashMap&lt;String, String&gt;();

public HashMap readFiles(String search, String dirPath) {

    snippets.clear();
    InputStream stream = null;

    //Method to get all the files in main and sub folders
    listDirectory(dirPath);

    for (int i = 0; i &lt; pathNames.size(); i++) {

        try {
            stream = new FileInputStream(pathNames.get(i));
            BufferedReader br = new BufferedReader(new    InputStreamReader(stream));

            Field chars = br.getClass().getDeclaredField("cb");
            chars.setAccessible(true);

            Field f = br.getClass().getDeclaredField("nChars"); 
            f.setAccessible(true);

            Field ff = br.getClass().getDeclaredField("nextChar");
            ff.setAccessible(true);

            String line;
            //not used
            int lineNum = 0;
            String path = pathNames.get(i);
            while ((line = br.readLine()) != null) {

                //Getting through to Java Source Code - uh oh

                // int charCount = (int) f.get(br);
                char[] cb = (char[]) chars.get(br);
                int nextChar = (Integer) ff.get(br);

                //not used
                lineNum++;

                if (line.contains(search)) {
                    String lines = "";
                    for (int t = nextChar - 200; t &lt; nextChar + 100; t++) {
                        lines += cb[t];
                    }

                    snippets.put(pathNames.get(i), lines);

                }
            }

        } catch (FileNotFoundException ex) {
            Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalArgumentException ex) {
            Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
        } catch (NoSuchFieldException ex) {
            Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
        } catch (SecurityException ex) {
            Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    return snippets;
}
//not used
        int lineNum = 0;

            //not used
            lineNum++;


So it's in your code because...?

stream = new FileInputStream(pathNames.get(i));


and then later...

String path = pathNames.get(i);


You could assign to string variable first, and then make the inputstream from the string variable.

    } catch (FileNotFoundException ex) {
        Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IOException ex) {
        Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalArgumentException ex) {
        Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
    } catch (NoSuchFieldException ex) {
        Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
    } catch (SecurityException ex) {
        Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
    }


Use multi-catch:

    } catch (FileNotFoundException | IOException | IllegalArgumentException | NoSuchFieldException | SecurityException ex) {
        Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
    }


Some of these might be subclasses and thus require removing. But there's no need to have the same line of code 6 times.



        Field f = br.getClass().getDeclaredField("nChars"); 
        f.setAccessible(true);


This field is unused as well.

                String lines = "";
                for (int t = nextChar - 200; t &lt; nextChar + 100; t++) {
                    lines += cb[t];
                }


Consider declaring a char[] variable and using System.arraycopy. Strings are immutable, so whatever performance benefit you had with ripping open the BufferedReader, you completely waste it and then some here. You can then put the char[] into a new String. 

snippets.put(pathNames.get(i), lines);


Why declare path if you're not going to use it?

Also, why do you not continue; here? HashMap.put will override any previous value, so you're only gonna see the last snippet of a file.

        stream = new FileInputStream(pathNames.get(i));
        BufferedReader br = new BufferedReader(new    InputStreamReader(stream));


None of these are getting closed.

Since you've got a try in there anyway, why not use try-with-resources?

String path = pathNames.get(i);
BufferedReader br;
try(stream = new FileInputStream(path);
        br = new BufferedReader(new InputStreamReader(stream))){
    ...
}


It works something like that.

private ArrayList&lt;String&gt; pathNames = new ArrayList();
private HashMap&lt;String, String&gt; snippets = new HashMap&lt;String, String&gt;();


Your variable definitions don't program to interfaces, so any setter would have to accept ArrayList only which makes your code hard to use for anyone else. Also, use diamond type inference - let the compiler get the generics from the type definition.

                char[] cb = (char[]) chars.get(br);
                int nextChar = (Integer) ff.get(br);


These reflection calls; you make them every iteration, but you only need them if the search string is found. Move them down.

Lastly, you should consider finding a way to get rid of the reflection. It's making the code slow, ugly and dangerous.



"Cleaned" version (will probably not compile; check syntax around try-with-resources and the multi-catch):

private List&lt;String&gt; pathNames = new ArrayList&lt;&gt;();
private Map&lt;String, String&gt; snippets = new HashMap&lt;&gt;();

public HashMap readFiles(String search, String dirPath) {

    snippets.clear();
    InputStream stream = null;
    BufferedReader br = null;

    //Method to get all the files in main and sub folders
    listDirectory(dirPath);

    for (int i = 0; i &lt; pathNames.size(); i++) {
        String path = pathNames.get(i);
        try (stream = new FileInputStream(path);
            br = new BufferedReader(new InputStreamReader(stream))){

            Field chars = br.getClass().getDeclaredField("cb");
            chars.setAccessible(true);

            Field ff = br.getClass().getDeclaredField("nextChar");
            ff.setAccessible(true);

            String line;
            while ((line = br.readLine()) != null) {
                if (line.contains(search)) {
                    //Getting through to Java Source Code - uh oh
                    char[] cb = (char[]) chars.get(br);
                    int nextChar = (Integer) ff.get(br);
                    char[] charBuffer = new char[300];
                    System.arraycopy(cb, nextChar - 200, charBuffer, 0, 300);
                    snippets.put(path, new String(charBuffer));
                    continue;

                }
            }

        } catch (FileNotFoundException | IOException | IllegalArgumentException | NoSuchFieldException | SecurityException ex) {
            Logger.getLogger(FileRead.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    return snippets;
}