### Description

`TransportFieldCapabilitiesAction` (used for the the `_field_caps` endpoint) has got a nested loop in `merge` method when unmapped fields are included (https://github.com/elastic/elasticsearch/blob/main/server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java#L245). This loops over indices -> fields -> indices, which could cause an exponential blowup in objects used when there's a large number of indices and unmapped fields.

We could be more efficient in creating & sharing IndexCap objects in these situations.
server/src/main/java/org/elasticsearch/action/fieldcaps/TransportFieldCapabilitiesAction.java
final Map<String, FieldCapabilitiesIndexResponse> indexResponses = Collections.synchronizedMap(new HashMap<>());
        
        final Map<String, Map<String, IndexFieldCapabilities>> indexMappingHashToResponses = Collections.synchronizedMap(new HashMap<>());
        final Consumer<FieldCapabilitiesIndexResponse> handleIndexResponse = resp -> {
            if (resp.canMatch() && resp.getIndexMappingHash() != null) {
                Map<String, IndexFieldCapabilities> curr = indexMappingHashToResponses.putIfAbsent(resp.getIndexMappingHash(), resp.get());
                if (curr != null) {
                    resp = new FieldCapabilitiesIndexResponse(resp.getIndexName(), resp.getIndexMappingHash(), curr, true);
                }
            }
            indexResponses.putIfAbsent(resp.getIndexName(), resp);
        };
        final FailureCollector indexFailures = new FailureCollector();
        
        final CountDown completionCounter = new CountDown(1 + remoteClusterIndices.size());
        final Runnable countDown = createResponseMerger(request, completionCounter, indexResponses, indexFailures, listener);
        final RequestDispatcher requestDispatcher = new RequestDispatcher(
            clusterService,
            transportService,
            task,
            request,
            localIndices,
            nowInMillis,
            concreteIndices,
            threadPool.executor(ThreadPool.Names.SEARCH_COORDINATION),
            handleIndexResponse,
            indexFailures::collect,
            countDown
        );
        requestDispatcher.execute();