During the development and review process of the disk health indicator we noticed that it was hard to understand. Since we learnt a lot from the first round of development we want to go through the code one more time and restructure it.
server/src/main/java/org/elasticsearch/cluster/routing/RoutingNode.java
* @param sizeGuess estimate for the number of shards that will be added to this instance to save re-hashing on subsequent calls to
     *                  {@link #add(ShardRouting)}
     */
    RoutingNode(String nodeId, @Nullable DiscoveryNode node, int sizeGuess) {
        this.nodeId = nodeId;
        this.node = node;
        this.shards = Maps.newLinkedHashMapWithExpectedSize(sizeGuess);
        this.relocatingShards = new LinkedHashSet<>();
        this.initializingShards = new LinkedHashSet<>();
        this.startedShards = new LinkedHashSet<>();
        this.shardsByIndex = Maps.newHashMapWithExpectedSize(sizeGuess);
        assert invariant();
    }
    private RoutingNode(RoutingNode original) {
        this.nodeId = original.nodeId;
        this.node = original.node;
        this.shards = new LinkedHashMap<>(original.shards);
        this.relocatingShards = new LinkedHashSet<>(original.relocatingShards);
        this.initializingShards = new LinkedHashSet<>(original.initializingShards);
        this.startedShards = new LinkedHashSet<>(original.startedShards);
        this.shardsByIndex = Maps.copyOf(original.shardsByIndex, HashSet::new);
        assert invariant();
    }
    RoutingNode copy() {
        return new RoutingNode(this);
    }
    @Override
    public Iterator<ShardRouting> iterator() {
        return Collections.unmodifiableCollection(shards.values()).iterator();
    }