void computeBackend(
    @NotNull String root,
    boolean isPrefix,
    boolean caseSensitive,
    BiFunction<? super @NotNull FileSystemProvider, ? super @Nullable FileSystem, @Nullable FileSystem> compute
  ) {
    myBackends.updateAndGet(oldList -> {
      List<@NotNull Backend> newList = new ArrayList<>(oldList);
      ListIterator<@NotNull Backend> iter = newList.listIterator();
      FileSystem newFs = null;
      while (iter.hasNext()) {
        Backend current = iter.next();
        if (current.root.equals(root)) {
          iter.remove();
          newFs = compute.apply(myProvider.myLocalProvider, current.fileSystem);
          if (newFs == null) {
            return newList;
          }
          break;
        }
      }

      if (newFs == null) {
        newFs = compute.apply(myProvider.myLocalProvider, null);
        if (newFs == null) {
          return newList;
        }
      }

      iter.add(new Backend(root, isPrefix, caseSensitive, newFs));

      // To ease finding the appropriate backend for a specific root, the roots should be ordered by their lengths in the descending order.
      // This operation is quite rare and the list is quite small. There's no reason to deal with error-prone bisecting.
      newList.sort((r1, r2) -> r2.root.length() - r1.root.length());

      return newList;
    });
  }
void computeBackend(
    @NotNull String root,
    boolean isPrefix,
    boolean caseSensitive,
    BiFunction<? super @NotNull FileSystemProvider, ? super @Nullable FileSystem, @Nullable FileSystem> compute
  )