private int editDistance(String word, int[] v0, int[] v1) {

  for (int i = 0; i < v0.length; i++) {
    v0[i] = i;
  }

  for (int i = 0; i < word.length(); i++) {
    v1[0] = i + 1;

    for (int j = 0; j < targetText.length(); j++) {
      int cost = (word.charAt(i) == targetText.charAt(j)) ? 0 : 1;
      v1[j + 1] = minimum(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost); // renvoie le min
    }

    int[] hold = v0;
    v0 = v1;
    v1 = hold;
  }

  return v0[targetText.length()];
}

public class DistanceTask implements Callable<DistancePair> {
  private final String targetText;
  private final int startOffset;
  private final int compareCount;

  public DistanceTask(String target, int offset, int count) {
    targetText = target;
    startOffset = offset;
    compareCount = count;
  }

  private int editDistance(String word, int[] v0, int[] v1) {
    // ...
  }

  @Override
  public DistancePair call() throws Exception {
    int[] v0 = new int[targetText.length() + 1];
    int[] v1 = new int[targetText.length() + 1];
    int bestIndex = -1;
    int bestDistance = Integer.MAX_VALUE;
    boolean single = false;

    for (int i = 0; i < compareCount; i++) {
      int distance = editDistance(knownWords[i + startOffset], v0, v1);

      if (bestDistance > distance) {
        bestDistance = distance;
        bestIndex = i + startOffset;
        single = true;
      } else if (bestDistance == distance) {
        single = false;
      }
    }

    return single ? new DistancePair(bestDistance, knownWords[bestIndex]) : new DistancePair(bestDistance);
  }
}

public class ThreadPoolDistance {
  private final ExecutorService threadPool;
  private final String[] knownWords;
  private final int blockSize;

  public ThreadPoolDistance(String[] words, int block) {
    threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    knownWords = words;
    blockSize = block;
  }

  @Override
  public DistancePair bestMatch(String target) {
    List<DistanceTask> tasks = new ArrayList<DistanceTask>();
    int size = 0;

    // Construction des sous-ensembles
    for (int base = 0; base < knownWords.length; base += size) {
      size = Math.min(blockSize, knownWords.length - base);
      tasks.add(new DistanceTask(target, base, size));
    }

    DistancePair best;

    try {
      // Invocation sur l'Executor
      List<Future<DistancePair>> results = threadPool.invokeAll(tasks);

      // Calcul du meilleur résultat, attente du retour de chaque Future
      best = DistancePair.worstMatch();
      
       for (Future<DistancePair> future: results) {
        DistancePair result = future.get();
        best = DistancePair.best(best, result);
      }

    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    } catch (ExecutionException e) {
      throw new RuntimeException(e);
    }
	
    return best;
  }
}

public class ForkJoinDistance {
  private ForkJoinPool threadPool = new ForkJoinPool();
  private final String[] knownWords;
  private final int blockSize;

  public ForkJoinDistance(String[] words, int block) {
    knownWords = words;
    blockSize = block;
  }

  public DistancePair bestMatch(String target) {
    return threadPool.invoke(new DistanceTask(target, 0, knownWords.length, knownWords));
  }

  public class DistanceTask extends RecursiveTask<DistancePair> {
    private final String compareText;
    private final int startOffset;
    private final int compareCount;
    private final String[] matchWords;

    public DistanceTask(String from, int offset, int count, String[] words) {
      compareText = from;
      startOffset = offset;
      compareCount = count;
      matchWords = words;
    }

    protected int minimum(int a, int b, int c) {
      return Math.min(Math.min(a, b), c);
    }

    private int editDistance(int index, int[] v0, int[] v1) {
      // ...
    }

    @Override
    protected DistancePair compute() {
      if (compareCount > blockSize) {
        // division en 2 sous-tâches
        int half = compareCount / 2;
        DistanceTask t1 = new DistanceTask(compareText, startOffset, half, matchWords);
        t1.fork();
        DistanceTask t2 = new DistanceTask(compareText, startOffset + half, compareCount - half, matchWords);
        DistancePair p2 = t2.compute();
        return DistancePair.best(p2, t1.join());
      }

      // comparaison
      int[] v0 = new int[compareText.length() + 1];
      int[] v1 = new int[compareText.length() + 1];
      int bestIndex = -1;
      int bestDistance = Integer.MAX_VALUE;
      boolean single = false;

      for (int i = 0; i < compareCount; i++) {
        int distance = editDistance(i + startOffset, v0, v1);
        
        if (bestDistance > distance) {
          bestDistance = distance;
          bestIndex = i + startOffset;
          single = true;
        } else if (bestDistance == distance) {
          single = false;
        }
      }
	  
      return single ? new DistancePair(bestDistance, knownWords[bestIndex]) : new DistancePair(bestDistance);
    }
  }
}

public class CompletableFutureDistance {
  private final List<ChunkDistanceChecker> chunkCheckers;
  private final int blockSize;

  public CompletableFutureDistance0(String[] words, int block) {
    blockSize = block;
    chunkCheckers = ChunkDistanceChecker.buildCheckers(words, block);
  }

  @Override
  public DistancePair bestMatch(String target) {
    List<CompletableFuture<DistancePair>> futures = new ArrayList<>();

    for (ChunkDistanceChecker checker: chunkCheckers) {
      CompletableFuture<DistancePair> future = CompletableFuture.supplyAsync(() -> checker.bestDistance(target));
      futures.add(future);
    }

    DistancePair best = DistancePair.worstMatch();

    for (CompletableFuture<DistancePair> future: futures) {
      best = DistancePair.best(best, future.join());
    }

    return best;
  }
}

 public DistancePair bestMatch(String target) {
  return chunkCheckers.stream()
         .map(checker -> CompletableFuture.supplyAsync(() -> checker.bestDistance(target)))
         .collect(Collectors.toList())
         .stream()
         .map(future -> future.join())
         .reduce(DistancePair.worstMatch(), (a, b) -> DistancePair.best(a, b));
}

public DistancePair bestMatch(String target) {
  return chunkCheckers.parallelStream()
         .map(checker -> checker.bestDistance(target))
         .reduce(DistancePair.worstMatch(), (a, b) -> DistancePair.best(a, b));
}