Can this graph search be optimized?
Rather than using recursion (which is pretty slow), you can for this using a Set of all reachable nodes, and a Queue of nodes you still haven't searched.  This is a basic implementation of Breadth-First Search.

private boolean isNodeReachableFrom(GraphNode start, GraphNode target) {

    // These are GraphNodes who's connected GraphNodes we haven't looked at yet.
    // Maybe the target is connected to one of these!
    Queue nodesToSearch = new LinkedList&lt;GraphNode&gt;();
    nodesToSearch.add(start);

    // These are GraphsNodes which we have proved are reachable from the given node.
    // If we are ever about to add the given target to this list, we return true.
    Set reachableNodes = new HashSet&lt;GraphNode&gt;();

    // As long as there are still nodesToSearch, we could still find our target.
    while (nodesToSearch.peek() != null) {
        GraphNode node = nodesToSearch.remove();

        // If we have already seen this node, we don't want to look at its connected
        // nodes again, because we could get stuck in a cycle.
        boolean isNewNode = reachableNodes.add(node);

        // If this is a new node, see if the target is connected.  If it is, we are
        // done successfully.  Otherwise, add all of the connected nodes to our
        // list of nodesToSearch.
        if (isNewNode) {
            for (GraphNode connectedNode : getConnectedNodes(node)) {
                if (connetedNode.equals(target)) {
                    return true;
                }
                nodesToSearch.add(connectedNode);
            }
        }
    }

    // There are no nodes we haven't searched, so the target is not reachable.
    return false;
}


Note: I made up getConnectedNodes(node) because your code didn't show me how to do this with the GraphNode object.

Note that this is an implementation of isNodeReachableFrom.  However, you seem to want to figure out the list of nodes which aren't reachable from a starting node.  Notice that we build up the list of all reachableNodes in the call above, which is probably what you really want.  You could write a function to return that reachableNodes structure, which would reuse the above logic without the target sections.  Something like this:

public Set&lt;GraphNode&gt; getReachableNodes(GraphNode start) { ... }