### 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/FieldCapabilities.java
if (search) {
                searchableIndices++;
            }
            if (agg) {
                aggregatableIndices++;
            }
            if (isDimension) {
                dimensionIndices++;
            }
            this.isMetadataField |= isMetadataField;
            
            
            if (indiceList.isEmpty()) {
                this.metricType = metricType;
            } else if (this.metricType != metricType) {
                hasConflictMetricType = true;
                this.metricType = null;
            }
            IndexCaps indexCaps = new IndexCaps(index, search, agg, isDimension, metricType);
            indiceList.add(indexCaps);
            for (Map.Entry<String, String> entry : meta.entrySet()) {
                this.meta.computeIfAbsent(entry.getKey(), key -> new HashSet<>()).add(entry.getValue());
            }
        }
        void getIndices(Collection<String> indices) {
            indiceList.forEach(cap -> indices.add(cap.name));
        }
        FieldCapabilities build(boolean withIndices) {
            final String[] indices;
            if (withIndices) {