Как удалить элементы из списка с лямбдой на основе другого списка

У меня есть список путей к файлу:.

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

54 выше относится к идентификатору файла

Затем я получаю a Set идентификаторов String, которые мое приложение может обрабатывать следующим образом:

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

Как я могу использовать Java 8 lambdas для filter всех элементов в filePaths, которые не содержат никаких приемлемых идентификаторов, которые содержатся в наборе коллекции acceptedIds.

Другими словами, я хотел бы сохранить в filePaths только те пути, которые имеют идентификаторы, которые находятся в acceptedIds. Например, 54 не находится в указанном выше списке, поэтому он удаляется.

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

Ответы

Ответ 1

Самый эффективный способ - извлечь идентификатор из пути, затем попытаться найти его в Set, чтобы каждый фильтр выполнялся в постоянное время, т.е. O(1), давая общий O(n), где n - это количество путей:

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

Если обратный подход выполняется, где каждый acceptedIds выполняется поиск в пути (как и в других ответах), каждый фильтр имеет значение O(m*k), где m - это число acceptedIds и k средняя длина пути, дающая общий O(n * m * k), который будет работать очень плохо для даже умеренных размеров коллекций.

Ответ 2

Вы можете написать:

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

Это фильтрует каждый путь таким образом, что хотя бы один из acceptedIds содержится в строковом представлении пути. Возможно, вы захотите реализовать что-то лучше, чем contains здесь, в зависимости от вашего прецедента (например, для соответствия имени файла).

anyMatch - это операция, которая определяет, соответствует ли хотя бы один элемент указанному предикату.

Обратите внимание, что в этом ответе не делается никаких предположений о пути фильтрации элементов. Если вы можете с уверенностью сказать, что в каждом пути родительский каталог имеет имя с идентификатором, вы обязательно должны пойти с ответом @Bohemian по причине производительности.

Ответ 3

Так же:

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;
}

Ответ 4

Если ваша структура имен файлов постоянна, я сначала использую регулярное выражение для извлечения числа, а затем проверяет, находится ли он в числе желаемых идентификаторов.

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"));
})
...