code: private Member[] pickTargets(Collection<Member> members, int startingIndex, int maxTargets) {
          final Member[] viableTargetArray = members.toArray(new Member[0]);
          final Member[] pickedTargets = new Member[Math.min(maxTargets, viableTargetArray.length)];
  
          for (int i = 0; i < pickedTargets.length; i++) {
              startingIndex = (startingIndex + 1) % viableTargetArray.length;
              pickedTargets[i] = viableTargetArray[startingIndex];
          }
          return pickedTargets;
      }
docs: Picks up to {@code maxTargets} from the provided {@code members}
  collection. The {@code startingIndex} parameter determines which
  subset of members can be returned. By increasing the parameter by the
  size of the returned array you can imitate a "sliding window", meaning
  that each time it is invoked it will rotate through a list of viable
  targets and return a sub-collection based on the previous method call.
  A member may be skipped if the collection of viable targets changes
  between two invocations but if the collection does not change,
  eventually all targets should be returned by this method.
  
  @param members       a collection of members to choose from
  @param startingIndex the index of the first returned member
  @param maxTargets    the maximum number of members to return
  @return the chosen targets
  @see CRDTReplicationConfig#getMaxConcurrentReplicationTargets()