### 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
}
            }
        };
    }
    private static FieldCapabilitiesRequest prepareRemoteRequest(
        FieldCapabilitiesRequest request,
        OriginalIndices originalIndices,
        long nowInMillis
    ) {
        FieldCapabilitiesRequest remoteRequest = new FieldCapabilitiesRequest();
        remoteRequest.setMergeResults(false); 
        remoteRequest.indicesOptions(originalIndices.indicesOptions());
        remoteRequest.indices(originalIndices.indices());
        remoteRequest.fields(request.fields());
        remoteRequest.filters(request.filters());
        remoteRequest.types(request.types());
        remoteRequest.runtimeFields(request.runtimeFields());
        remoteRequest.indexFilter(request.indexFilter());
        remoteRequest.nowInMillis(nowInMillis);
        return remoteRequest;
    }
    private FieldCapabilitiesResponse merge(
        Map<String, FieldCapabilitiesIndexResponse> indexResponsesMap,
        FieldCapabilitiesRequest request,
        List<FieldCapabilitiesFailure> failures
    ) {
        final List<FieldCapabilitiesIndexResponse> indexResponses = indexResponsesMap.values()
            .stream()
            .sorted(Comparator.comparing(FieldCapabilitiesIndexResponse::getIndexName))
            .toList();