private static List<Node> topologicalSort(Collection<Node> nodes) {
        Map<ComponentId, Integer> numIncoming = new HashMap<>();

        nodes.forEach(
                node -> node.usedComponents().forEach(
                        injectedNode -> numIncoming.merge(injectedNode.componentId(), 1, (a, b) -> a + b)));

        LinkedList<Node> sorted = new LinkedList<>();
        List<Node> unsorted = new ArrayList<>(nodes);

        while (!unsorted.isEmpty()) {
            List<Node> ready = new ArrayList<>();
            List<Node> notReady = new ArrayList<>();
            unsorted.forEach(node -> {
                if (numIncoming.getOrDefault(node.componentId(), 0) == 0) {
                    ready.add(node);
                } else {
                    notReady.add(node);
                }
            });

            if (ready.isEmpty()) {
                throw new IllegalStateException("There is a cycle in the component injection graph: " + findCycle(notReady));
            }

            ready.forEach(node -> node.usedComponents()
                    .forEach(injectedNode -> numIncoming.merge(injectedNode.componentId(), -1, (a, b) -> a + b)));
            sorted.addAll(0, ready);
            unsorted = notReady;
        }
        return sorted;
    }
The returned list is the nodes from the graph bottom-up.

For each iteration, the algorithm finds the components that are not "wanted by" any other component,
and prepends those components into the resulting 'sorted' list. Hence, the first element in the returned
list is the component that is directly or indirectly wanted by "most" other components.

@return A list where a earlier than b in the list implies that there is no path from a to b
container-core/src/main/java/com/yahoo/container/di/componentgraph/core/ComponentGraph.java