web-dev-qa-db-fra.com

Comment supprimer des éléments d'une liste avec lambda basé sur une autre liste

J'ai la liste des chemins de fichiers:.

List<Path> filePaths; //e.g. [src\test\resources\file\15\54\54_exampleFile.pdf]

54 ci-dessus fait référence à l'ID du fichier

J'obtiens alors un Set de String Ids que mon application peut gérer comme suit:

Set<String> acceptedIds = connection.getAcceptedIDs(); //e.g. elements [64, 101, 33]

Comment puis-je utiliser Java 8 lambdas pour filter sur tous les éléments dans filePaths qui ne contiennent aucun des identifiants acceptables contenus dans acceptedIds Ensemble de collection.

En d'autres termes, je voudrais conserver dans filePaths uniquement les chemins qui ont des identifiants qui sont dans acceptedIds set. Par exemple, 54 ne figure pas dans la liste ci-dessus et est donc supprimé.

filePaths.stream().filter(...).collect(Collectors.toList());
20
M06H

Le moyen le plus efficace consiste à extraire l'ID du chemin, puis à essayer de le trouver dans l'ensemble, en exécutant chaque filtre en temps constant, c'est-à-dire O(1) donnant un O(n) global, où n est le nombre de chemins:

filePaths.stream()
  .filter(p -> acceptedIds.contains(p.getParent().getFileName().toString()))
  .collect(Collectors.toList());

Si l'approche inverse est effectuée, où chaque acceptedIds est recherché dans le chemin (comme dans les autres réponses), chaque filtre est O(m*k), où m est le nombre de acceptedIds et k est la longueur moyenne du chemin, donnant un O(n * m * k) global, qui fonctionnera très mal pour des tailles de collections même modérées.

24
Bohemian

Vous pourriez écrire:

filePaths.stream()
         .filter(p -> acceptedIds.stream().anyMatch(id -> p.toString().contains(id)))
         .collect(toList());

Cela filtre chaque chemin de telle sorte qu'au moins un des acceptedIds soit contenu dans la représentation sous forme de chaîne du chemin. Vous voudrez peut-être implémenter quelque chose de mieux que contains ici, selon votre cas d'utilisation (correspondant au début du nom de fichier par exemple).

anyMatch est une opération qui détermine si au moins un élément correspond au prédicat donné.

Notez que cette réponse ne fait aucune hypothèse sur le chemin pour filtrer les éléments. Si vous pouvez dire en toute sécurité que dans chaque chemin, le répertoire parent est nommé avec l'id, vous devez absolument utiliser la réponse @Bohemian, pour des raisons de performances.

3
Tunaki

Ainsi:

List removeMissing(List l1, List l2) {
    List ret = l1.stream()
        .filter(o -> l2.contains(o)) //Keep if object o satisfies the condition "l2 contains a reference to this object"
        .collect(Collectors.toList());
    return ret;
}
1
Xirema

Si la structure de votre nom de fichier est constante, j'utiliserais d'abord une expression régulière pour extraire le nombre, puis vérifierait s'il figure parmi les identifiants souhaités.

final Set<String> acceptedIds = ...
// Matches the number of the file, concluded with the underscore
final Pattern extractor = Pattern.compile("\.*(?<number>\d+)_")
filePaths.stream().filter( path -> {
    final Matcher m = extractor
        .matcher(path.getFileName().toString());
    m.find();
    return acceptedIds.contains(m.group("number"));
})
...
0
Danilo Pianini